「CF149D」括号涂色 区间DP好题

题目描述

Petya遇到了一个关于括号序列的问题: 给定一个字符串 S S S,它代表着正确的括号序列,即 “ ( ( (” 与 “ ) ) )” 是匹配的。例如:“ ( ( ) ) ( ) (())() (())()” 和 “ ( ) () ()”是正确的,“ ) ( ) )() )()”与“ ( ( ) (() (()”则不是正确的。 在正确的括号序列中,一个左边的括号一定是匹配一个右边的括号(反之亦然)。例如,在下图中,第 3 3 3 个括号匹配第 6 6 6 个括号,第 4 4 4 个括号匹配第 5 5 5 个括号。
现在你需要对一个正确的括号序列做涂色操作,严格满足以下三个条件:

1、每个括号要么不涂色,要么涂红色,要么涂蓝色。

2、一对匹配的括号需要且只能将其中一个涂色。

3、相邻的括号不能涂上同一种颜色(但是可以都不涂颜色)。

求:给整个括号序列涂上颜色的方案数,答案可能比较大,对 1 0 9 + 7 10^9+7 109+7 取模。

输入格式

输入的第一行包含一个字符串 s s s,( 2 < = ∣ s ∣ < = 700 2 <= |s| <= 700 2<=s<=700),代表一个正确的括号序列。

输出格式

输出方案数。(对 1 0 9 + 7 10^9 + 7 109+7 取模)

样例

输入
(()())
输出
40

分析

读者不难想到这是一道区间DP。当我们定义 d p [ l ] [ r ] dp[l][r] dp[l][r]为左端点为 l l l,右端点为 r r r的方案总数会发现,我们不知道某个点到底是什么颜色,规则的第二点和第三点就不好计算。
不妨令 d p [ l ] [ r ] [ p ] [ q ] dp[l][r][p][q] dp[l][r][p][q]为左端点为 l l l,右端点为 r r r,且 a [ l ] a[l] a[l]的颜色为 c o l o r [ p ] color[p] color[p] a [ r ] a[r] a[r]的颜色为 c o l o r [ q ] color[q] color[q]的方案总数。(我写的 c o l o r [ 0 ] color[0] color[0]是无色)

DP_1

l e n = 2 len=2 len=2,此时有

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

d p [ l ] [ r ] dp[l][r] dp[l][r]中,若 a [ l ] a[l] a[l] a [ r ] a[r] a[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;

至于为什么,我不想多说了,,,其实就是把所有可行的情况加起来。当然你也可以用四重循环。

DP_3

若不匹配,可以找到与 a [ l ] a[l] a[l]匹配的右括号 a [ k ] a[k] a[k](用栈 s t a c k stack stack),把 d p [ l ] [ r ] dp[l][r] dp[l][r]分成 d p [ l ] [ k ] dp[l][k] dp[l][k] d p [ k + 1 ] [ r ] dp[k+1][r] dp[k+1][r],我们如果还像上面那样一个一个列举,会很累的…用四重循环枚举 a [ l ] a[l] a[l] a [ k ] a[k] a[k] a [ k + 1 ] a[k+1] a[k+1] a [ r ] a[r] a[r]的颜色,只用剪掉 a [ k ] a[k] a[k] a [ k + 1 ] a[k +1] a[k+1]颜色相同的情况就行了。当然情况二用这个也会简单很多。( l ≤ k ≤ r l \leq k \leq r lkr

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;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值