题目描述
Petya遇到了一个关于括号序列的问题: 给定一个字符串
,它代表着正确的括号序列,即 “
” 与 “
” 是匹配的。例如:“
” 和 “
”是正确的,“
”与“
”则不是正确的。 在正确的括号序列中,一个左边的括号一定是匹配一个右边的括号(反之亦然)。例如,在下图中,第
个括号匹配第
个括号,第
个括号匹配第
个括号。
现在你需要对一个正确的括号序列做涂色操作,严格满足以下三个条件:
1、每个括号要么不涂色,要么涂红色,要么涂蓝色。
2、一对匹配的括号需要且只能将其中一个涂色。
3、相邻的括号不能涂上同一种颜色(但是可以都不涂颜色)。
求:给整个括号序列涂上颜色的方案数,答案可能比较大,对 取模。
输入格式
输入的第一行包含一个字符串 ,( ),代表一个正确的括号序列。
输出格式
输出方案数。(对 取模)
样例
输入
(()())
输出
40
分析
读者不难想到这是一道区间DP。当我们定义
为左端点为
,右端点为
的方案总数会发现,我们不知道某个点到底是什么颜色,规则的第二点和第三点就不好计算。
不妨令
为左端点为
,右端点为
,且
的颜色为
,
的颜色为
的方案总数。(我写的
是无色)
DP_1
若 ,此时有
dp[l][r][1][0] = 1;
dp[l][r][0][1] = 1;
dp[l][r][2][0] = 1;
dp[l][r][0][2] = 1;
DP_2
在 中,若 和 是一组匹配的括号,那么
dp[l][r][1][0] = (dp[l + 1][r - 1][2][2] + dp[l + 1][r - 1][0][0] + dp[l + 1][r - 1][0][1] + dp[l + 1][r - 1][2][0] + dp[l + 1][r - 1][0][2] + dp[l + 1][r - 1][2][1]) % Mod;
dp[l][r][2][0] = (dp[l + 1][r - 1][1][2] + dp[l + 1][r - 1][0][0] + dp[l + 1][r - 1][0][1] + dp[l + 1][r - 1][1][0] + dp[l + 1][r - 1][0][2] + dp[l + 1][r - 1][1][1]) % Mod;
dp[l][r][0][1] = (dp[l + 1][r - 1][2][2] + dp[l + 1][r - 1][0][0] + dp[l + 1][r - 1][1][0] + dp[l + 1][r - 1][2][0] + dp[l + 1][r - 1][0][2] + dp[l + 1][r - 1][1][2]) % Mod;
dp[l][r][0][2] = (dp[l + 1][r - 1][2][1] + dp[l + 1][r - 1][0][0] + dp[l + 1][r - 1][1][0] + dp[l + 1][r - 1][0][1] + dp[l + 1][r - 1][2][0] + dp[l + 1][r - 1][1][1]) % Mod;
至于为什么,我不想多说了,,,其实就是把所有可行的情况加起来。当然你也可以用四重循环。
DP_3
若不匹配,可以找到与 匹配的右括号 (用栈 ),把 分成 和 ,我们如果还像上面那样一个一个列举,会很累的…用四重循环枚举 、 、 、 的颜色,只用剪掉 和 颜色相同的情况就行了。当然情况二用这个也会简单很多。( )
for(int o = 0; o < 3; o ++) {
for(int p = 0; p < 3; p ++) {
for(int q = 0; q < 3; q ++) {
for(int s = 0; s < 3; s ++) {
if((p == 1 && q == 1) || (p == 2 && q == 2)) continue;
dp[l][r][o][s] = (dp[l][r][o][s] + dp[l][Match[l]][o][p] * dp[Match[l] + 1][r][q][s]) % Mod;
}
}
}
}
代码
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <stack>
#define LL long long
using namespace std;
const int Mod = 1000000007, MAXN = 705;
char a[MAXN];
int n, r, Match[MAXN];
LL dp[MAXN][MAXN][3][3], ans;
stack <int> sk;
int main() {
// freopen("coloring.in", "r", stdin);
// freopen("coloring.out", "w", stdout);
scanf("%s", a + 1);
n = strlen(a + 1);
for(int i = 1; i <= n; i ++) {
if(a[i] == '(') sk.push(i);
else {
Match[sk.top()] = i;
sk.pop();
}
}
for(int len = 2; len <= n; len ++) {
for(int l = 1; l <= (n - len + 1); l ++) {
r = l + len - 1;
if(l == (r - 1)) {
dp[l][r][1][0] = 1; dp[l][r][0][1] = 1; dp[l][r][2][0] = 1; dp[l][r][0][2] = 1;
}
else if(Match[l] == r) {
dp[l][r][1][0] = (dp[l + 1][r - 1][2][2] + dp[l + 1][r - 1][0][0] + dp[l + 1][r - 1][0][1] + dp[l + 1][r - 1][2][0] + dp[l + 1][r - 1][0][2] + dp[l + 1][r - 1][2][1]) % Mod;
dp[l][r][2][0] = (dp[l + 1][r - 1][1][2] + dp[l + 1][r - 1][0][0] + dp[l + 1][r - 1][0][1] + dp[l + 1][r - 1][1][0] + dp[l + 1][r - 1][0][2] + dp[l + 1][r - 1][1][1]) % Mod;
dp[l][r][0][1] = (dp[l + 1][r - 1][2][2] + dp[l + 1][r - 1][0][0] + dp[l + 1][r - 1][1][0] + dp[l + 1][r - 1][2][0] + dp[l + 1][r - 1][0][2] + dp[l + 1][r - 1][1][2]) % Mod;
dp[l][r][0][2] = (dp[l + 1][r - 1][2][1] + dp[l + 1][r - 1][0][0] + dp[l + 1][r - 1][1][0] + dp[l + 1][r - 1][0][1] + dp[l + 1][r - 1][2][0] + dp[l + 1][r - 1][1][1]) % Mod;
}
else if(l <= Match[l] && Match[l] <= r) {
for(int o = 0; o < 3; o ++) {
for(int p = 0; p < 3; p ++) {
for(int q = 0; q < 3; q ++) {
for(int s = 0; s < 3; s ++) {
if((p == 1 && q == 1) || (p == 2 && q == 2)) continue;
dp[l][r][o][s] = (dp[l][r][o][s] + dp[l][Match[l]][o][p] * dp[Match[l] + 1][r][q][s]) % Mod;
}
}
}
}
}
}
}
for(int i = 0; i <= 2; i ++) {
for(int j = 0; j <= 2; j ++) {
ans = (ans + dp[1][n][i][j]) % Mod;
}
}
printf("%lld", ans);
return 0;
}