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\)式,希望能對大家的對\(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;
}