暴力DP可以定义 f[i][a][b][c] f [ i ] [ a ] [ b ] [ c ] 表示到第i个数为止,以0结尾的最长胜利序列长度为a,以1结尾的最长胜利序列长度为b,以2结尾的最长胜利序列长度为c的方案数,显然这样过于暴力了。接下来,我们会发现一个结论,那就是a,b,c任意两个数相差不超过2,证明如下:
由于石头的上一个必然是布,所以 c>=a−1 c >= a − 1 ,同理, a>=b−1 a >= b − 1 , b>=c−1 b >= c − 1 ,所以任意两个之间相差不会超过2.
这样,状态树就被大大压缩。
时间复杂度: O(αn)(α O ( α n ) ( α 为常数)
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 2006
#define tt 998244353
using namespace std;
int n,cnt,f[maxn][maxn][5][5];
bool vis[maxn][3];
int abs(int x){return (x>0)?x:-x;}
int main(){
freopen("B.in","r",stdin);
freopen("B.out","w",stdout);
scanf("%d",&n);getchar();
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++){
char ch=getchar();
while(ch!='\n'&&ch!=EOF)vis[i][ch-'0']=1,ch=getchar();
}
f[0][0][2][2]=1;
for(int i=0;i<=n;i++)
for(int p0=0;p0<=i;p0++)
for(int j=-2;j<=min(2,i-p0);j++)
for(int k=-2;k<=min(2,i-p0);k++) if(abs(j-k)<=2){
int p,p1=p0+j,p2=p0+k;if(p1<0||p2<0)continue;
if(vis[i+1][0])p=max(p0,p2+1),(f[i+1][p][p1-p+2][p2-p+2]+=f[i][p0][j+2][k+2])%=tt;
if(vis[i+1][1])(f[i+1][p0][max(1,j)+2][k+2]+=f[i][p0][j+2][k+2])%=tt;
if(vis[i+1][2])(f[i+1][p0][j+2][max(j+1,k)+2]+=f[i][p0][j+2][k+2])%=tt;
}
for(int i=1;i<=n;i++){
cnt=0;
for(int p0=i-2;p0<=i;p0++)
for(int j=-2;j<=i-p0;j++)
for(int k=-2;k<=i-p0;k++){
int p1=p0+j,p2=p0+k;if(p0<0||p1<0||p2<0||(p0<i&&p1<i&&p2<i))continue;
(cnt+=f[n][p0][j+2][k+2])%=tt;
}
printf("%d ",cnt);
}
return 0;
}