括号涂色题解(区间DP)

题目:

Petya遇到了一个关于括号序列的问题: 给定一个字符串S,它代表着正确的括号序列,即(“(”)与 (“)”)是匹配的。例如:“(())()” 和 “()”是正确的,“)()”与“(()”则不是正确的。 在正确的括号序列中,一个左边的括号一定是匹配一个右边的括号(反之亦然)。例如,在下图中,第 3 个括号匹配第 6 个括号,第 4 个括号匹配第 5 个括号。
在这里插入图片描述

现在你需要对一个正确的括号序列做涂色操作,严格满足以下三个条件:

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

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

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

求:给整个括号序列涂上颜色的方案数,答案可能比较大,对 1000000007 取模。

输入1:

(())

输出1:

12

输入2:

(()())

输出2:

40

输入3:

()

输出3:

4

题解:

首先由于是括号,我们需要知道它的左括号下标及右括号下标,存入数组方便后面用,方法是用栈,详细见代码:

void p() {
	for(int i = 0;i < n; i++) {
		if(a[i] == '(') {
			s.push(i);//右括号输入进去
		}
		else{
			b[i] = s.top();//遇到对应左括号弹出,再对下标保存
			b[s.top()] = i;
			s.pop();
		}
	}	
	return;
}

首先这道题的状态很不一样,令dp[i][j][k][l]为i括号取k种类颜色,和j括号取l种类颜色的方案总数。dp[i][j][k][l]应等于:
1.i+1==j(i括号与j括号相邻)
取法为一个无色一个颜色二选一

dp[i][j][1][0] = 1;
dp[i][j][0][1] = 1;
dp[i][j][2][0] = 1;
dp[i][j][0][2] = 1;

2.若b[i] == j(当i括号与j括号对应时):
首先求得元区间dp[i + 1][j - 1][k][l]的方案数, 用来求一个无色,一个随便取方式比如:dp[i][j][0][1],再加上dp[i + 1][j - 1][k][l] (注意l不能为1,因为若为1,则j-1号位为一号颜色,而j号位又是一号颜色所以相邻两个重复,舍去),其它三个完全同理思考,见代码:

if(j != 1) {
	dp[qi][zhong][0][1] += dp[qi + 1][zhong - 1][i][j];
	dp[qi][zhong][0][1] %= mod;
}
if(i != 1) {
	dp[qi][zhong][1][0] += dp[qi + 1][zhong - 1][i][j];
	dp[qi][zhong][1][0] %= mod;
}
if(j != 2) {
	dp[qi][zhong][0][2] += dp[qi + 1][zhong - 1][i][j];
	dp[qi][zhong][0][2] %= mod;
}
if(i != 2) {
	dp[qi][zhong][2][0] += dp[qi + 1][zhong - 1][i][j];
	dp[qi][zhong][2][0] %= mod;
}

3.若b[i] != j(当i括号与j括号不对应时)
dp[i][j][k][l]+=(i ~ s[i]对应的右括号下标即b[i]的任意颜色组合方案数) * (s[i]对应的右括号下标的下一位即b[i] + 1 ~ j的任意颜色组合方案数)
详细见代码:

for(int i = 0;i < 3; i++) {
	for(int j = 0;j < 3; j++) {
		for(int l = 0;l < 3; l++) {
			for(int m = 0;m < 3; m++) {
				if(l != m || l == 0 || m == 0) {
					dp[qi][zhong][i][j] += (dp[qi][k][i][l] * dp[k + 1][zhong][m][j]) % mod;
					dp[qi][zhong][i][j]%=mod;
				}
			}
		}
	}
}//注意为什么是四重,因为为了让两括号颜色错开(不相同),所以用了四重

代码:

#include <iostream>
#include <cstdio>
#include <stack>
#include <cstring>
using namespace std;
#define mod 1000000007
long long dp[705][705][3][3];
int b[705], n, sum = 0;
char a[705];
stack<int>s;
void p() {
	for(int i = 0;i < n; i++) {
		if(a[i] == '(') {
			s.push(i);
		}
		else{
			b[i] = s.top();
			b[s.top()] = i;
			s.pop();
		}
	}	
	return;
}
void d(int qi, int zhong) {
	if(qi + 1 == zhong) {
		dp[qi][zhong][1][0] = 1;
		dp[qi][zhong][0][1] = 1;
		dp[qi][zhong][2][0] = 1;
		dp[qi][zhong][0][2] = 1;
		return;
	}
	if(b[qi] == zhong) {
		d(qi + 1, zhong - 1);
		for(int i = 0;i < 3; i++) {
			for(int j = 0;j < 3; j++) {
				if(j != 1) {
					dp[qi][zhong][0][1] += dp[qi + 1][zhong - 1][i][j];
					dp[qi][zhong][0][1] %= mod;
				}
				if(i != 1) {
					dp[qi][zhong][1][0] += dp[qi + 1][zhong - 1][i][j];
					dp[qi][zhong][1][0] %= mod;
				}
				if(j != 2) {
					dp[qi][zhong][0][2] += dp[qi + 1][zhong - 1][i][j];
					dp[qi][zhong][0][2] %= mod;
				}
				if(i != 2) {
					dp[qi][zhong][2][0] += dp[qi + 1][zhong - 1][i][j];
					dp[qi][zhong][2][0] %= mod;
				}
			}
		}
		return;
	}
	int k = b[qi];
	d(qi, k);
	d(k + 1, zhong);
	for(int i = 0;i < 3; i++) {
		for(int j = 0;j < 3; j++) {
			for(int l = 0;l < 3; l++) {
				for(int m = 0;m < 3; m++) {
					if(l != m || l == 0 || m == 0) {
						dp[qi][zhong][i][j] += (dp[qi][k][i][l] * dp[k + 1][zhong][m][j]) % mod;
						dp[qi][zhong][i][j]%=mod;
					}
				}
			}
		}
	}
}
int main() {
	scanf("%s", a);
	n = strlen(a);
	p();
	d(0, n - 1);//由于字符串从0开始所以这里改成0~n-1
	long long sum = 0;
	for(int i = 0;i < 3; i++) {
		for(int j = 0;j < 3; j++) {
			sum = (sum + dp[0][n - 1][i][j]) % mod;//同上
		}
	}
	printf("%lld", sum % mod);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/cqbz_lipengcheng/article/details/107451570