CF268D Wall Bars
Solution
给我的感觉就是很暴力的计数DP。
因为再暴力,这也算个DP
那么我们可以显然的构造出一个状态 \(f_{i,a,b,c,1/0}\) ,表示现在是第 \(i\) 个踏板放在某个面上,其它三个面的下一个踏板距离这个的距离为 \(a,b,c\) ,当前这个踏板是/否能从地面到达。
在此我们是优化了一维,也就是将 \(i\) 踏板所在面下一个距离这个踏板的距离优化成了 \(1/0\) ,因为能到达的时候距离是 \(<h\) 的,不能时是 \(\geq h\) 的,也是两个状态。
再思考另一个问题,开了五维数组,那么空间复杂度是 \(O(n\times n\times n\times n\times 2)=O(n^4)\) ,显然是会炸的。我们还得优化空间,发现题目中说明 \(h\leq \min(n,30)\) ,所以可以将 \(a,b,c\) 三维的空间变成30,当距离 \(\geq h\) 时,赋为 \(h\) 。此时为 \(O(2\cdot 30^3\cdot n)\) ,完全可以。
再看时间复杂度。发现我们在优化空间的时候,把时间复杂度也降低了。本来要枚举五维,复杂度是 \(O(n^5)\) ,现在也变成了 \(O(2\cdot 30^3\cdot n)\) 。
现在终于到了最重要的转移方程:
\[f_{i+1,a+1,b+1,c+1,1/0}+=f_{i,a,b,c,1/0}
\]
这个是还在本来的那个面上
\[f_{i+1,1/h,b+1,c+1,[a<h]}+=f_{i,a,b,c,1/0}
\]
这是换到另一个面
\[f_{i+1,a+1,1/h,c+1,[b<h]}+=f_{i,a,b,c,1/0},
f_{i+1,a+1,b+1,1/h,[c<h]}+=f_{i,a,b,c,1/0}
\]
这两个同理。
答案随便求求即可。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=1e9+9;
int n,h,ans;
int f[1010][31][31][31][2];
#define add(i,a,b,c,j,val) (f[i][min(a,h)][min(b,h)][min(c,h)][j]+=val)%=mod;
ll read(){
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
return x*f;
}
int main(){
n=read();h=read();
f[1][1][1][1][1]=4;
for(int i=1;i<=n;i++)
for(int a=1;a<=h;a++)
for(int b=1;b<=h;b++)
for(int c=1;c<=h;c++)
for(int j=0;j<2;j++)
if(f[i][a][b][c][j]){
add(i+1,a+1,b+1,c+1,j,f[i][a][b][c][j]);
int d=1;if(j==0) d=h;
add(i+1,d,b+1,c+1,a<h,f[i][a][b][c][j]);
add(i+1,a+1,d,c+1,b<h,f[i][a][b][c][j]);
add(i+1,a+1,b+1,d,c<h,f[i][a][b][c][j]);
}
for(int a=1;a<=h;a++)
for(int b=1;b<=h;b++)
for(int c=1;c<=h;c++)
for(int j=0;j<2;j++)
if(j||a<h||b<h||c<h) ans=(ans+f[n][a][b][c][j])%mod;
printf("%d\n",ans);
}