CF149D 【Coloring Brackets】
这道题本质上不难,但是很麻烦,很恶心。
我看了一下,市面上的题解基本都是用\(dfs\)的方式来演绎这个\(dp\),而且有一些雷同于单调,下面,我用区间\(dp\)的角度来演绎这道题。
我们姑且先定义\(dp_{i,j,0}\)为对区间\([i,j]\)染色的方案数。
我们先考虑转移,然后根据转移再完善我们的定义。
考虑两种情况
1、对于一个括号串,他由一对单调的括号里面加上一些串组成
2、对于一个括号串,他由数对单调的括号里面加上一些串组成
对于第一种情况:
在外面的一对括号串可以有四种方式进行染色,但是却存在冲突。因为相邻的两个括号颜色不能相同,那么根据融斥的思想,我们想要算出冲突的情况。因为对于每一种外层染色的情况,我们都可以把与他相邻的一个括号染成固定的颜色,从而达到冲突的目的。
由此,为了避免计算逆元我们定义\(dp_{i,j,1}\)为区间\([i,j]\)中串首染一种固定颜色的方案总数。得到转移方程如下:
$\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ $ \(dp_{i,j,0}\) = \(4 * (dp_{i+1,j-1,0} - dp_{i+1,j-1,1})\)
但是\(dp_{i,j,1}\)怎么转移呢?我们给串首固定一种颜色,那么包含在里面的串当且仅当在里面的串的串首与他颜色一致,减去即可。
$\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ $ \(dp_{i,j,1}\) = \(dp_{i+1,j-1,0} - dp_{i+1,j-1,1}\)
对于第二种情况:
根据乘法原理,先找到第一个单调括号的结尾,然后将两段相乘,再减去冲突部分,即两段相接的地方一样的时候,即为两段首颜色确定且相同的情况,有两种颜色,所以\(*2\)
令\(f\)为交界处,则有\(dp\)式为:
$\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ $ \(dp_{i,j,0}\) = \(dp_{i,f,0} * dp_{f+1,j,0} - dp_{i,f,1} * dp_{f+1,j,1} * 2\)
对于\(dp_{i,j,1}\)很简单,首位确定就与后面没什么关系了,直接乘上即可。
$\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ $ \(dp_{i,j,1}\) = \(dp_{i,f,1} * dp_{f,j,0}\)
以上是我根据排列组合的思想推出的\(dp\)式,希望能对大家的对\(dp\)的推导与理解有帮助。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int mod = 1000000007;
char s[705];
int dp[705][705][2] , n;
int pd(int x , int y) { // 用栈的方式判断。
int f = 1 , top = 0;
for (int i = x; i <= y; ++i) {
if(s[i] == '(') top ++;
else top --;
if(top < 0) return 0;
if(!top && i != y && f == 1) f = i;
}
if(top != 0) return 0;
return f;
}
int main() {
scanf("%s" , s + 1);
n = strlen(s + 1);
for (int len = 2; len <= n; len += 2) {
for (int i = 1; i <= n; ++i) {
int j = i + len - 1;
if(len == 2) {
if(s[i] == '(' && s[j] == ')') {
dp[i][j][0] = 4;
dp[i][j][1] = 1;//特判
}
continue;
}
int f = pd(i , j);//这个地方的判断可以在之前预处理出来,但我看见不预处理也能过就不管了。
if(!f) continue;
else if(f == 1) {
dp[i][j][0] = (4ll * (long long)(dp[i + 1][j - 1][0] - dp[i + 1][j - 1][1]) % mod + mod) % mod;
dp[i][j][1] = ((dp[i + 1][j - 1][0] - dp[i + 1][j - 1][1]) % mod + mod) % mod;
}
else {
dp[i][j][0] = (long long)dp[i][f][0] * (long long)dp[f + 1][j][0] % mod - (long long) dp[i][f][1] * (long long)dp[f + 1][j][1] * 2ll % mod;
dp[i][j][0] = (dp[i][j][0] % mod + mod) % mod;
dp[i][j][1] = (long long)dp[i][f][1] * (long long)dp[f + 1][j][0] % mod;
}//dp转移
}
}
printf("%d" , dp[1][n][0]);
return 0;
}