cf上一道超狠的dp

D. Minesweeper 1D
time limit per test
2 seconds
memory limit per test
512 megabytes
input
standard input
output
standard output

Game "Minesweeper 1D" is played on a line of squares, the line's height is 1 square, the line's width is n squares. Some of the squares contain bombs. If a square doesn't contain a bomb, then it contains a number from 0 to 2 — the total number of bombs in adjacent squares.

For example, the correct field to play looks like that: 001*2***101*. The cells that are marked with "*" contain bombs. Note that on the correct field the numbers represent the number of bombs in adjacent cells. For example, field 2* is not correct, because cell with value 2 must have two adjacent cells with bombs.

Valera wants to make a correct field to play "Minesweeper 1D". He has already painted a squared field with width of n cells, put several bombs on the field and wrote numbers into some cells. Now he wonders how many ways to fill the remaining cells with bombs and numbers are there if we should get a correct field in the end.

Input

The first line contains sequence of characters without spaces s1s2... sn (1 ≤ n ≤ 106), containing only characters "*", "?" and digits "0", "1" or "2". If character si equals "*", then the i-th cell of the field contains a bomb. If character si equals "?", then Valera hasn't yet decided what to put in the i-th cell. Character si, that is equal to a digit, represents the digit written in the i-th square.

Output

Print a single integer — the number of ways Valera can fill the empty cells and get a correct field.

As the answer can be rather large, print it modulo 1000000007 (109 + 7).

Sample test(s)
Input
?01???
Output
4
Input
?
Output
2
Input
**12
Output
0
Input
1
Output
0
Note

In the first test sample you can get the following correct fields: 001**1, 001***, 001*2*, 001*10.

这题的难度我已无话可说……

先贴代码,再分析:

#include<iostream>
#include<cstring>
#include<utility>
#include<vector>
#include<algorithm>
#include <functional>
#include<list>
#include<iterator>
#include<deque>
#include<set>
#include<map>
#include<stack>
#include<queue>
#include<cstdio>
#include<cmath>
#define Maxn 1000010
#define mod 1000000007
using namespace std;
class string_cmp
{
public:
    bool operator()(const char *str1,const char *str2) const
    {
        return strcmp(str1,str2)<0;
    }
};
int dp[Maxn][3];
char str[Maxn];
int main()
{
    cin>>str;
    int len=strlen(str);
    if(str[0]=='0'||str[0]=='?') dp[1][0]=1;
    if(str[0]=='*'||str[0]=='?') dp[1][1]=1;
    if(str[0]=='1'||str[0]=='?') dp[1][2]=1;
    for(int i=2;i<=len;i++){
        if(str[i-1]=='0'||str[i-1]=='?') dp[i][0]=dp[i-1][0];
        if(str[i-1]=='1'||str[i-1]=='?'){
            dp[i][2]=dp[i-1][0];
            dp[i][0]=(dp[i][0]+dp[i-1][1])%mod;
        }
        if(str[i-1]=='2'||str[i-1]=='?') 
            dp[i][2]=(dp[i][2]+dp[i-1][1])%mod;
        if(str[i-1]=='*'||str[i-1]=='?')
            dp[i][1]=(dp[i-1][1]+dp[i-1][2])%mod;
    }
    cout<<(dp[len][0]+dp[len][1])%mod<<endl;
    return 0;
}

首先说一下:dp数组的含义,dp[i][j]表示前i的字符的j状态,j=0表示平衡态,有两种……0和……*1;j=1,表示多*态,有……*一种;j=2,表示缺*态,有……*2和……1(1前无*),……表示正确的任意组合,这对后面的决策已经没有影响了,故不考虑。

然后开始分析:

当遇到下一个字符,比如'0',那么根据游戏规则,0前不能是多*态和缺*态,因此可以肯定只能是平衡态,并且两种平衡态都符合要求,加上'0'后变成平衡态,故转移式dp[i][0]=dp[i-1][0];

再如'1',那么根据游戏规则,1前可以是平衡态或者多*态,并且多*态加上1后均变成了平衡态,故转移式dp[i][0]=dp[i-1][1],平衡态加上1后变成缺*态,故转移式dp[i][2]=dp[i-1][0];;

再如'2’,那么根据游戏规则,2前只能是多*态,并且加上2后,变成缺*态,故转移式dp[i][2]=dp[i-1][1];

再如'*',那么根据游戏规则,*前可以是多*态或者缺*态,加上*后均变成多*态,注意:缺*态+*不等于平衡态,因为这个*既可以与前面结合,又可以与后面结合,你要是在这里犯错误,那前面的都全白推了,所有的努力白费了,你还检查不出程序的错误,就悲剧了,故转移式dp[i][1]=(dp[i-1][1]+dp[i-1][2])%mod;

再如'?',那就相当复杂了,它可以替代任何字符,因此上面所有的转移式它都可以有;

但是,你会发现上面的程序和这有点不一样,那是因为已经合并过很多式子了,如果觉得看得有压力,那就直白翻译,分'?'和非'?'两种情况,再逐个击破,相信也能AC。

注意:初值要小心,这步再错,更加悲剧:第一个不可能是2,因此2的后,初始全0,这种三个都为0,转移下去必然为0,这样不可能状态就得到了,其余情况:如0,是平衡态,因此dp[1][0]=1;如1:,是缺*态,因此dp[1][2]=1;如*,是多*态,因此dp[1][1]=1;这三个都是合法状态,因此必然有一个dp不为0的,也能为转移到一下一个状态做好准备。如'?',那么上面都必须赋值为1,因为'?'可以替代任何字符。

最后的结果,当然是0或者*1结尾的平衡态,或者*结尾的多*态,01或者*2这样无法结尾,因此没有缺*态,所以输出<<(dp[len][0]+dp[len][1])%mod;

再者,记住只有'?'号才能发生相加情况,其余全是赋值,因为其余都是确定的方案,而'?'号可以多重选择,根据排列组合分类原理,是加法原理……合并时要小心,要全局看整个程序,特别是'?'执行完的下一句,统一转移时,可以统一的原因在于
初值为0,前面非'?'的,下一步相加,就等价于+0,其实就是赋值,'?'的前面已覆盖值,下一步相加,就等价于两类情况相加。
这个必须发上一贴,不然明天我连自己怎么想的都忘了,研究了这么长时间,再忘记,岂不是亏大了!!!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值