[10.10模拟] mine

题意:

有一个 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
", (g[n+1][0]+g[n+1][1])%mo);
  return 0;
}
原文地址:https://www.cnblogs.com/HLXZZ/p/7648280.html