1. 程式人生 > 實用技巧 >CF149D 【Coloring Brackets】

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