隐藏
「雅礼day2」扫雷mine - 动态规划 | Bill Yang's Blog

路终会有尽头,但视野总能看到更远的地方。

0%

「雅礼day2」扫雷mine - 动态规划

题目大意

有一个 1 维的扫雷游戏,每个格子用*表示有雷,用 0/1/2 表示无雷并且相邻格子中有 0/1/2 个雷。
给定一个仅包含?、*、0、1、2 的字符串 s,问有多少种方法将所有的?改为*/0/1/2 使其合法。


题目分析

因为题目没有特殊的限制,且向左的转移可以转为向右转移,因此我们可以按照从左到右的阶段进行动态规划。
设状态$f[i,j]$表示前$i$个格子,第$i$个格子的状态为$j$的方案数。
其中状态$j$表示的意义如下:

  • $j=0$:当前格子为$0$
  • $j=2$:当前格子为$2$
  • $j=3$:当前格子为$*$
  • $j=4$:当前格子为$1$,且这个$1$表示右方有雷
  • $j=4$:当前格子为$1$,且这个$1$表示左方有雷

然后我们就可以转移了,处理一下限制关系即可。
注意边界是不可能存在向外指的$1$或$2$的。


代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
typedef long long LL;
inline const LL Get_Int() {
LL num=0,bj=1;
char x=getchar();
while(x<'0'||x>'9') {
if(x=='-')bj=-1;
x=getchar();
}
while(x>='0'&&x<='9') {
num=num*10+x-'0';
x=getchar();
}
return num*bj;
}
string s;
int n;
const LL mod=1000000007;
LL f[1000005][6];
//0,1 ,2,3 ,4 ,5
//0,NUL,2,雷,向右指的1,向左指的1
int main() {
ios::sync_with_stdio(false);
cin>>s;
n=s.length();
s=' '+s;
if(s[1]=='?')f[1][0]=f[1][4]=f[1][3]=1;
if(s[1]=='*')f[1][3]=1;
if(s[1]=='0')f[1][0]=1;
if(s[1]=='1')f[1][4]=1;
for(int i=2; i<=n; i++)
if(s[i]=='?') {
f[i][0]=(f[i-1][0]+f[i-1][5])%mod;
f[i][3]=(f[i-1][4]+f[i-1][2]+f[i-1][3])%mod;
f[i][5]=f[i-1][3];
if(i<n) {
f[i][2]=f[i-1][3];
f[i][4]=(f[i-1][0]+f[i-1][5])%mod;
}
} else if(s[i]=='*')f[i][3]=(f[i-1][4]+f[i-1][2]+f[i-1][3])%mod;
else if(s[i]=='0')f[i][0]=(f[i-1][0]+f[i-1][5])%mod;
else if(s[i]=='1') {
f[i][4]=(f[i-1][0]+f[i-1][5])%mod;
f[i][5]=f[i-1][3];
} else if(s[i]=='2')f[i][2]=f[i-1][3];
printf("%lld\n",(f[n][0]+f[n][3]+f[n][5])%mod);
return 0;
}
姥爷们赏瓶冰阔落吧~