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

题目描述

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

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

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

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

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

输入格式

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

输出格式

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

样例

输入

(()())

输出

40

分析

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

DP_1

l e n = 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] 中,若 a [ l ] a[l] 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 [ k ] a[k] (用栈 s t a c k stack ),把 d p [ l ] [ r ] dp[l][r] 分成 d p [ l ] [ k ] dp[l][k] d p [ k + 1 ] [ r ] dp[k+1][r] ,我们如果还像上面那样一个一个列举,会很累的…用四重循环枚举 a [ l ] a[l] a [ k ] a[k] a [ k + 1 ] a[k+1] a [ r ] a[r] 的颜色,只用剪掉 a [ k ] a[k] a [ k + 1 ] a[k +1] 颜色相同的情况就行了。当然情况二用这个也会简单很多。( l k r l \leq k \leq 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;
			}
		}
	}
}

代码

#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;
}

猜你喜欢

转载自blog.csdn.net/Clever_Hard/article/details/107450298