原題:
http://172.16.0.132/senior/#contest/show/2061/3
題目描述:
小A正在搭積木。有N個位置可以讓小A使用,初始高度都為0。小A每次搭積木的時候,都會選定一個擁有相同高度的區(qū)間[A..B],然后將位置[A+1..B-1]上的所有積木的高度加一。不幸的是,小A把積木搭好之后沒多久,小A調(diào)皮的弟弟就將其中若干個位置上的積木弄倒了。小A想知道他原來的積木是如何擺放的,所以他求助于你,請你告訴他原來有多少種可能的擺法。
輸入:
第一行為一個正整數(shù)N,表示小A有N個位置。
第二行有N個由空格分隔的整數(shù)Hi,表示第i個位置的積木高度。-1表示這個位置上的積木已經(jīng)被弄倒了
輸出:
唯一的一行,輸出包括可能的擺法mod 1,000,000,007的結(jié)果。
樣例輸入:
輸入1:
3
-1 2 -1
輸入2:
-1 -1 -1
輸入3:
6
-1 -1 -1 2 -1 -1
樣例輸出:
輸出1:
0
輸出2:
2
輸出3:
3
數(shù)據(jù)范圍限制:
對于50%的數(shù)據(jù) 1<=N<=1000 -1<=Hi<=1000
對于80%的數(shù)據(jù) 1<=N<=10000
對于100%的數(shù)據(jù) 1<=N<=20000 -1<=Hi<=10000
分析:
對于每一個位置,求出這一個位置的極高,例如對于7而言,每一個位置的極高為0,1,2,3,2,1,0,對于8而言,每一個位置的極高為0,1,2,3,3,2,1,0;
設f[i][j]表示到第i個位置,高度為j的方案數(shù),如果當前位置被推倒,則f[i][j]=f[i-1][j-1]+f[i-1][j]+f[i-1][j+1];對于知道當前位置的高度時,上式所有的j=a[i];注意用滾動dp做,否則數(shù)組會炸!!!
這樣的時間復雜度是O(n^2)
優(yōu)化
在所有程序中,取mod運算時很慢的,所以盡量減少取mod運算,每10次取1次mod即可
實現(xiàn)
#include<cstdio>
#include<cstring>
int tt,t,n,i,j,a[20001];
long long f[2][10001];
int h(int x)
{
if(x<n/2+1) return x-1;
return n-x;
}
int main()
{
freopen("brick.in","r",stdin);freopen("brick.out","w",stdout);
scanf("%d",&n);
for(i=1;i<=n;i++) scanf("%d",&a[i]);
f[0][0]=1;
for(i=1;i<=n;i++)
{
t=h(i);
tt=h(i-1);
if(a[i]!=-1)
{
if(a[i]<=t)
{
f[1][a[i]]=f[1][a[i]]+f[0][a[i]];
if(a[i]>=1) f[1][a[i]]=f[1][a[i]]+f[0][a[i]-1];
if(a[i]+1<=tt) f[1][a[i]]=f[1][a[i]]+f[0][a[i]+1];
if(i%10==0) f[1][a[i]]%=1000000007;
}
}
else
{
for(j=0;j<=t;j++)
{
f[1][j]=f[1][j]+f[0][j];
if(j>=1) f[1][j]=f[1][j]+f[0][j-1];
if(j+1<=tt) f[1][j]=f[1][j]+f[0][j+1];
if(i%10==0) f[1][j]%=1000000007;
}
}
memcpy(f[0],f[1],sizeof(f[1]));
memset(f[1],0,sizeof(f[1]));
}
printf("%lld",f[0][0]%1000000007);
}