题目
链接:https://ac.nowcoder.com/acm/problem/53683
来源:牛客网
输入一个长为n(n<=1e6)的字符串,第i个字符可能是以下的几种之一,求最终可能的方案数
0:这个格子没有烈焰,且其左右两个格子均没有烈焰
1:这个格子没有烈焰,且其左右两个格子中只有一个烈焰
2:这个格子没有烈焰,且其左右两个格子中均有烈焰
*:这个格子有烈焰
?:未告诉你本格情况
思路来源
https://blog.nowcoder.net/n/2ae2547c386145e3ae59664bf0b440a7
题解
发现i的状态仅和i-1、i+1有关,于是我最开始做这个题,
最简单粗暴的思路就是开三维,带着所有状态跑
0-3的4种状态分别对应0 1 2 *,dp[N][4][4][4],然后发现开不下,
枚举中间点i,特判一下两端的情形,把N给滚动了,就搞过去了
补了一下,发现有更巧妙的做法,
首先,转移的时候,其实只有i和i+1两个状态即可,从i-1和i的状态转移过来
其次,可以把012三种数字给合并,表示不是雷的状态,用当前数字约束转移
dp[i][0-3]分别表示dp[i][00/01/10/11],其中0表示这一格不是雷,
初始,dp[0][00]=dp[0][01]=1,认为0处没有雷,此时不知道1的情况,故都有可能,
dp[i][00]可以理解成i这一格不是雷,如果i+1这格也不是雷的方案数,其余同理,
先把所有可能的情况都更新一下,i+1的字母冲突了,则自然不会从这个状态转移过来
最后,dp[n][00]+dp[n][10]就是答案,因为n+1不是雷
问号的情形,对应了012*四种方案之和
代码1
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10,mod=1e9+7;
int n,dp[N][4];//00 01 10 11
char s[N];
int main(){
scanf("%s",s+1);
n=strlen(s+1);
dp[0][0]=dp[0][1]=1;
for(int i=1;i<=n;++i){
if(s[i]=='0'){
dp[i][0]=dp[i-1][0];
}
else if(s[i]=='1'){
dp[i][0]=dp[i-1][2];
dp[i][1]=dp[i-1][0];
}
else if(s[i]=='2'){
dp[i][1]=dp[i-1][2];
}
else if(s[i]=='*'){
dp[i][2]=(dp[i-1][1]+dp[i-1][3])%mod;
dp[i][3]=(dp[i-1][1]+dp[i-1][3])%mod;
}
else if(s[i]=='?'){
dp[i][0]=(dp[i-1][0]+dp[i-1][2])%mod;
dp[i][1]=(dp[i-1][0]+dp[i-1][2])%mod;
dp[i][2]=(dp[i-1][1]+dp[i-1][3])%mod;
dp[i][3]=(dp[i-1][1]+dp[i-1][3])%mod;
}
//printf("i:%d 0:%d 1:%d 2:%d 3:%d\n",i,dp[i][0],dp[i][1],dp[i][2],dp[i][3]);
}
printf("%d\n",(dp[n][0]+dp[n][2])%mod);
return 0;
}
代码2(我的乱搞)
#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7,N=1e6+10;
int n,dp[2][4][4][4],ans,f;
char s[N];
//3:*
bool ok(int x,int y){
if(x==2)return 0;
if(x==1 && y!=3)return 0;
if(x==0 && y)return 0;
if(x==3 && (y!=1 && y!=3))return 0;
if(y==2)return 0;
if(y==1 && x!=3)return 0;
if(y==0 && x)return 0;
if(y==3 && (x!=1 && x!=3))return 0;
return 1;
}
bool ok(int x,int y,int z){
if(y==0){
if(!(x!=3 && z!=3))return 0;//左右有雷
}
if(y==1){
if(x==3 && z==3)return 0;//左右两个雷
if(!(x==3 || z==3))return 0;//左右没雷
}
if(y==2){
if(!(x==3 && z==3))return 0;//左右不是两个雷
}
if(y==3){
if(x==0 || z==0)return 0;//左右有0
}
return 1;
}
void g(char v,int &x,int &y){
if(v=='*'){x=y=3;return;}
if(v=='?'){x=0;y=3;return;}
x=y=v-'0';
}
void add(int &x,int y){
x+=y;
if(x>=mod)x-=mod;
}
int main(){
scanf("%s",s+1);
n=strlen(s+1);
if(n==1){
if(s[1]=='?')puts("2");
else if(s[1]=='0' || s[1]=='*')puts("1");
else puts("0");
return 0;
}
else if(n==2){
int pa,pb,qa,qb;
g(s[1],pa,pb);g(s[2],qa,qb);
for(int j=pa;j<=pb;++j){
for(int k=qa;k<=qb;++k){
if(!ok(j,k))continue;
ans++;
}
}
printf("%d\n",ans);
return 0;
}
int j,pa,pb,qa,qb,ra,rb;
g(s[1],qa,qb);g(s[2],ra,rb);
for(int k=qa;k<=qb;++k){
for(int l=ra;l<=rb;++l){
if(k==3)j=1;
else j=0;
if(!ok(j,k,l))continue;
dp[f][j][k][l]=1;
//printf("j:%d k:%d l:%d dp:%d\n",j,k,l,dp[f][j][k][l]);
}
}
for(int i=2;i<=n-1;++i){
f^=1;
g(s[i-1],pa,pb);g(s[i],qa,qb);g(s[i+1],ra,rb);
for(int j=pa;j<=pb;++j){
for(int k=qa;k<=qb;++k){
for(int l=ra;l<=rb;++l){
if(!ok(j,k,l))continue;
for(int m=0;m<4;++m){
if(!ok(m,j,k))continue;
add(dp[f][j][k][l],dp[f^1][m][j][k]);
}
//printf("j:%d k:%d l:%d ",j,k,l);out(j,k,l);
//printf("i:%d j:%d k:%d l:%d dp:%d\n",i,j,k,l,dp[f][j][k][l]);
}
}
}
memset(dp[f^1],0,sizeof dp[f^1]);
}
f^=1;
g(s[n-1],qa,qb);g(s[n],ra,rb);
for(int k=qa;k<=qb;++k){
for(int l=ra;l<=rb;++l){
if(l==3)j=1;
else j=0;
if(!ok(k,l,j))continue;
for(int m=0;m<4;++m){
if(!ok(m,k,l))continue;
add(dp[f][k][l][j],dp[f^1][m][k][l]);
}
add(ans,dp[f][k][l][j]);
//printf("k:%d l:%d j:%d dp:%d\n",k,l,j,dp[f][k][l][j]);
}
}
printf("%d\n",ans);
return 0;
}