题意:
有一个 1 维的扫雷游戏,每个格子用表示有雷,用 0/1/2 表示无雷并且相邻格子中有 0/1/2 个雷。给定一个仅包含?、、0、1、2 的字符串 s,问有多少种方法将所有的?改为*/0/1/2 使其合法。
题解:
dp
这题告诉我,设计好一个状态是多么重要
考场上设dp[i][4],转移了一个上午,到最后还是拍wa了,只好弃疗
看了菊神的dp状态,感觉我是头猪
g[i][0]表示到第i位,这一位一定不选雷,前一位不是雷的方案数,g[i][1]表示这一位一定不选雷,前一位是雷的方案数,f[i]表示这一位一定选雷的方案数
这样设状态就没有局限性了,根据*/0/1/2的性质直接转移就可以了,我那样一个一个设是有局限性的
ps:dp的时候用状态转移的思想有时会更加方便,这使我领略到了dp和递推的区别(请允许我瞎yy)
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
#define N 1000010
#define mo 1000000007
using namespace std;
ll f[N],g[N][2];
char s[N];
int gi() {
int x=0,o=1; char ch=getchar();
while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
if(ch=='-') o=-1,ch=getchar();
while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
return o*x;
}
int main() {
scanf("%s", s+1);
int n=strlen(s+1);
if(s[1]=='*') g[2][1]++;
if(s[1]=='0') g[2][0]++;
if(s[1]=='1') f[2]++;
if(s[1]=='?') f[2]+=2,g[2][0]++,g[2][1]++;
for(int i=2; i<=n; i++) {
if(s[i]=='0') g[i+1][0]=g[i][0];
else if(s[i]=='1') f[i+1]=g[i][0],g[i+1][0]=g[i][1];
else if(s[i]=='2') f[i+1]=g[i][1];
else if(s[i]=='*') f[i+1]=f[i],g[i+1][1]=f[i];
else {
g[i+1][0]=(g[i][0]+g[i][1])%mo;
g[i+1][1]=f[i];
f[i+1]=(g[i][0]+g[i][1]+f[i])%mo;
}
}
printf("%lld\n", (g[n+1][0]+g[n+1][1])%mo);
return 0;
}