Codeforces 404D(dp)

传送门

题意:

1 1 1维扫雷问题。

给你一个长度为 l e n len len的字符串,每一个字符串分别有五种可能性: 1 1 1 0 0 0 ? ? ? 2 2 2 ∗ * ‘,

  • 如果字符为 1 1 1,则代表其相邻有一个 ∗ *

  • 如果字符为 0 0 0,则代表相邻没有 ∗ *

  • 如果字符为 2 2 2,则代表相邻有两个 ∗ *

  • 如果字符为 ∗ * ,则代表此处是地雷

  • 如果字符为 ? ? ?,则代表有该字符能够表示为其他四种字符。

现在问你给定的字符串中能够表示多少种正确的扫雷图。并对答案 m o d    1 0 9 + 7 \mod10^9+7 mod109+7

题解:

刚拿到这个题就觉得这个题很 d p dp dp,奈何 d p dp dp还是弱项 … … \dots \dots

我们分析,对于除了 ? ? ?的四种字符,要使得原来的串合法,则必定需要满足:

如果当前的字符为 0 0 0,则其前面必定要是 0 0 0或者 ∗ 1 *1 1

如果当前的字符为 2 2 2,则其前面必定要是 ∗ *

如果当前的字符为 ∗ * ,则其前面必定 ∗ * 或者 2 2 2或者 01 01 01

如果当前的字符为 1 1 1,则前面可能是 0 0 0亦可能是 ∗ *

我们发现,此时,我们可以将上述的合法条件设置成 4 4 4个不同的状态,即分别为:

0 ∗ 1 01 2 ∗ 0 \quad *1 \quad 01 \quad 2 \quad * 01012

我们发现,其他的合法的状态必定可以通过增加某个字符达到上诉4个状态。

因此我们设 d p [ i ] [ j ] dp[i][j] dp[i][j]代表前 i i i个字符串中,当前处于状态 j j j时的可能性。

而根据上述的合理条件,我们不难写出一下几条状态转移方程:

如果当前字符为 0 0 0,有
d p [ i ] [ 0 ] + = d p [ i − 1 ] [ 0 ] ∣ ∣ d p [ i ] [ 0 ] + = d p [ i − 1 ] [ 1 ] dp[i][0]+=dp[i-1][0]\quad || \quad dp[i][0]+=dp[i-1][1] dp[i][0]+=dp[i1][0]dp[i][0]+=dp[i1][1]

如果当前字符为 2 2 2,有
d p [ i ] [ 3 ] + = d p [ i − 1 ] [ 4 ] dp[i][3]+=dp[i-1][4] dp[i][3]+=dp[i1][4]

如果当前字符为 ∗ * ,有
d p [ i ] [ 4 ] + = d p [ i − 1 ] [ 2 ] ∣ ∣ d p [ i ] [ 4 ] + = d p [ i − 1 ] [ 3 ] ∣ ∣ d p [ i ] [ 4 ] + = d p [ i − 1 ] [ 4 ] dp[i][4]+=dp[i-1][2] \quad || \quad dp[i][4]+=dp[i-1][3] \quad ||\quad dp[i][4]+=dp[i-1][4] dp[i][4]+=dp[i1][2]dp[i][4]+=dp[i1][3]dp[i][4]+=dp[i1][4]

如果当前字符为 1 1 1,有
d p [ i ] [ 1 ] + = d p [ i − 1 ] [ 4 ] ∣ ∣ d p [ i ] [ 2 ] + = d p [ i − 1 ] [ 0 ] ∣ ∣ d p [ i ] [ 2 ] + = d p [ i − 1 ] [ 1 ] dp[i][1]+=dp[i-1][4] \quad || \quad dp[i][2]+=dp[i-1][0] \quad || \quad dp[i][2]+=dp[i-1][1] dp[i][1]+=dp[i1][4]dp[i][2]+=dp[i1][0]dp[i][2]+=dp[i1][1]

最后如果当前的字符为 ? ? ?显然上述可能性均有。

最后我们需要注意开头和结尾位置的特判。

当处于开头时,显然只存在 ∗ * 0 0 0的状态。

而位于结尾时,显然只存在 ∗ * 0 0 0 ∗ 1 *1 1的状态。

最后只需要线性转移并统计答案即可。

#include <bits/stdc++.h>
#define maxn 1000005
using namespace std;
char str[maxn];
int dp[maxn][6];
const int mod=1e9+7;
typedef long long ll;
int main()
{
    scanf("%s",str);
    int len=strlen(str);
    if(str[0]=='?') dp[0][0]=1,dp[0][2]=1,dp[0][4]=1;
    if(str[0]=='0') dp[0][0]=1;
    if(str[0]=='*') dp[0][4]=1;
    if(str[0]=='1') dp[0][3]=1;
    for(int i=1;i<len;i++){
        if(str[i]=='0'||str[i]=='?'){
            dp[i][0]=(dp[i][0]+dp[i-1][0])%mod;
            dp[i][0]=(dp[i][0]+dp[i-1][1])%mod;
        }
        if(str[i]=='1'||str[i]=='?'){
            dp[i][1]=(dp[i][1]+dp[i-1][4])%mod;
            dp[i][2]=(dp[i][2]+dp[i-1][0])%mod;
            dp[i][2]=(dp[i][2]+dp[i-1][1])%mod;
        }
        if(str[i]=='2'||str[i]=='?'){
            dp[i][3]=(dp[i][3]+dp[i-1][4])%mod;
        }
        if(str[i]=='*'||str[i]=='?'){
            dp[i][4]=(dp[i][4]+dp[i-1][2])%mod;
            dp[i][4]=(dp[i][4]+dp[i-1][3])%mod;
            dp[i][4]=(dp[i][4]+dp[i-1][4])%mod;
        }
    }
    ll res=dp[len-1][0]+dp[len-1][1];
    res%=mod;
    res=(res+dp[len-1][4])%mod;
    cout<<res<<endl;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值