博客
关于我
「CF149D」括号涂色 区间DP好题
阅读量:329 次
发布时间:2019-03-04

本文共 4607 字,大约阅读时间需要 15 分钟。

题目描述

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

转载地址:http://ixve.baihongyu.com/

你可能感兴趣的文章
做做Java
查看>>
攻防世界新手区pwn
查看>>
2020-2021新技术讲座课程
查看>>
GIT简介
查看>>
eclipse github团队成员修改工程后push推送
查看>>
shell中的数学运算
查看>>
shell 数学运算
查看>>
如何使用4G模块通过MQTT协议传输温湿度数据到onenet
查看>>
图解:网络硬件的发展史
查看>>
map的find函数和count函数
查看>>
C++并发与多线程(一)
查看>>
C++ 并发与多线程(五)
查看>>
STM32--USART串口收发数据
查看>>
逆合成孔径雷成像(一)— 傅里叶变换基础1
查看>>
elf格式静态链接和动态链接
查看>>
openthread编译错误:error: could not find ctags
查看>>
7628 EDCCA认证寄存器修改(认证自适应)
查看>>
C#四行代码写简易计算器,超详细带注释(建议新手看)
查看>>
计算机网络子网划分错题集
查看>>
java一些基本程序
查看>>