牛客OI周赛13-提高组A-0还是1(简单DP)
链接:https://ac.nowcoder.com/acm/contest/2970/A
来源:牛客网
题目描述
给出一个长度为n的操作序列。每个位置为“&”,“|”,“”中的一个。&表示按位与,|表示按位或,表示按位异或。
定义一个长度为n+1的01数列(即该数列只包含0和1)是合法的,当且仅当将该操作序列插入这个长度为n+1的数列后,运算结果为1。
具体的讲,如果给出的操作序列为“&|”,将其插入到序列1010中进行运算:1&0|10=1。所以1010就是操作序列“&|^”的一个合法序列。
这里的运算不考虑运算符之间的优先级,即全部为自左向右依次进行运算。
现在你需要统计对于一个给定的操作序列,有多少个合法序列。
由于答案可能很大,你只需要输出答案对1e9+7取模后的结果。
输入描述:
第一行一个正整数n,表示给出的操作序列的长度。
第二行一个长度为n的字符串,表示给出的操作序列。
输出描述:
一行一个整数,表示答案对1e9+7取模后的结果。
示例1
输入
复制
3
&|^
输出
复制
8
说明
合法的01序列有:
0001
0010
0101
0110
1001
1010
1100
1110
DP。设dp[i] [j] [k] 为当前遍历到第i个操作符,此操作符后面的运算数选择j(j为0或者1),得到的结果为k(k为0或1)的数列总数。
根据三个运算符,很容易就能写出转移方程。
记得初始化要正确。
#include <iostream>
#define mod 1000000007
using namespace std;
string s;
int n;
int dp[10000005][2][2] = { 0 };//dp[i][j]表示在进行了第i个操作符 当前选择j(j=1 or 0) 得到 k 的数字序列数
int main()
{
freopen("data.txt", "r", stdin);
cin >> n;
cin >> s;
s = ' ' + s;
dp[0][1][1] = dp[0][0][0] = 1;//正确初始化
for(int i = 1; i <= n; i++)
{
if(s[i] == '&')
{
dp[i][0][0] = (((dp[i - 1][0][0] + dp[i - 1][0][1]) % mod + dp[i - 1][1][0]) % mod + dp[i - 1][1][1]) % mod;
dp[i][0][1] = 0;//显然不存在这种情况
dp[i][1][0] = (dp[i - 1][1][0] + dp[i - 1][0][0]) % mod;
dp[i][1][1] = (dp[i - 1][1][1] + dp[i - 1][0][1]) % mod;
}
else if(s[i] == '|')
{
dp[i][0][0] = (dp[i - 1][1][0] + dp[i - 1][0][0]) % mod;
dp[i][0][1] = (dp[i - 1][1][1] + dp[i - 1][0][1]) % mod;
dp[i][1][0] = 0;//显然不存在这种情况
dp[i][1][1] = (((dp[i - 1][0][0] + dp[i - 1][0][1]) % mod + dp[i - 1][1][0]) % mod + dp[i - 1][1][1]) % mod;
}
else if(s[i] == '^')
{
dp[i][0][0] = (dp[i - 1][1][0] + dp[i - 1][0][0]) % mod;
dp[i][0][1] = (dp[i - 1][1][1] + dp[i - 1][0][1]) % mod;
dp[i][1][0] = (dp[i - 1][1][1] + dp[i - 1][0][1]) % mod;
dp[i][1][1] = (dp[i - 1][1][0] + dp[i - 1][0][0]) % mod;
}
}
// for(int i = 1; i <= n; i++)
// {
// cout << dp[i][0][0] << ' ' << dp[i][0][1] << ' ' << dp[i][1][0] << ' ' << dp[i][1][1] << endl;
// }
cout << (dp[n][1][1] + dp[n][0][1]) % mod;
return 0;
}