题目链接:D. Minesweeper 1D 【一维扫雷】
题意:
一维扫雷,每个位置可能为'0', '1', '2', '*'。‘*’表示雷,0 1 2 都表示其左右的雷的数量,现在给出一个一维扫雷的地图(|s|<=1e6),其中有一些字符'?',这些‘?’可以是以上四个值的任意一种,问有多少种合法的地图。
输入样例:
?01???
输出样例:
4
输入样例:
?
输出样例:
2
题目分析:
对于每个位置,要么是雷,要么不是雷,但是数字会限制其左右的雷的数量。
如果前一个位置为数字,假设左边情况已经是已知的,那么这一位是否为雷也是确切的。
如果前一个位置为雷,那么这个位置可以为雷,也可以为数字。
定义一下三种状态,
0:这一位不是雷。下一位不是雷。
1:这一位不是雷。下一位是雷。
2:这一位是雷。下一位可以是雷,也可以不是雷。
dp初始条件是dp[0][0] = dp[1][0] = 1;
初始位置下标1前面是没有雷的,但是下标1可以是雷也可以不是雷
那么转移dp[i]的方式就取决于s[i]是什么了,分类讨论即可
代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN = 1000005;
const int INF = 0x3f3f3f3f;
const int mod = 1000000007;
int n, p;
typedef pair<int,int> P;
char s[MAXN];
int dp[MAXN][3];
//0:x/x 1:x/* 2:*/?
inline void ADD(int &a, int &b){
a += b; if(a >= mod) a-= mod;
}
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' || s[i] == '?'){
ADD(dp[i][0], dp[i-1][0]);
}
if(s[i] == '1' || s[i] == '?'){
ADD(dp[i][1], dp[i-1][0]);
ADD(dp[i][0], dp[i-1][2]);
}
if(s[i] == '2' || s[i] == '?'){
ADD(dp[i][1], dp[i-1][2]);
}
if(s[i] == '*' || s[i] == '?'){
ADD(dp[i][2], dp[i-1][1]);
ADD(dp[i][2], dp[i-1][2]);
}
}
ADD(dp[n][0],dp[n][2]);
printf("%d\n",dp[n][0]);
return 0;
}