題目鏈接:https://codeforces.com/contest/149/problem/D
題意:給你一個合法的括號序列,現在讓你給這個序列染色,染色的條件如下:
- 每對()有且只有一個括號被染色
- 相鄰的括號如果都被染色了,那麼其顏色不能相同
- 每個括號只能塗藍色,紅色,或者不塗任何顏色
現在讓你求染色方案總數,並且最後結果mod 1e9+7
算法思路:
- 依據每對括號進行dp,因爲括號存在嵌套,所以需要使用dfs進行遞歸處理
- 爲了知道每個左括號對應右括號,需要提前進行預處理
- dp狀態方程爲dp[l][r][i][j] 表示區間【l,r】的左右兩個端點分別塗顏色i和顏色j方案數
- 需要注意區間是一對括號和不是一對括號這兩種情況對應的染色方案不一樣。注意狀態轉移的那部分如果不確定一定是一個區間端點匹配的情況,都染色情況的限制都較弱於區間端點配對的情況,詳情見代碼
代碼:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 800;
const ll mod = 1e9 + 7;
ll dp[maxn][maxn][3][3];
string s;
ll tail[maxn];//tail[i]等於某個位置的左括號對應的右括號的位置
void dfs(int l, int r) {
if(l >= r)
return;
if(l + 1 == r) {
for(int i = 0; i < 3; i++)
for(int j = 0; j < 3; j++) {
if(i != 0 && j != 0 || (i == 0 && j == 0))
continue;
dp[l][r][i][j] = 1;
}
return;
}
if(tail[l] == r) {
dfs(l + 1, r - 1);
for(int i = 0; i < 3; i++) {
for(int j = 0; j < 3; j++) {
if(i == 0 && j == 0 || (i != 0 && j != 0))
continue;
for(int k = 0; k < 3; k++) {
for(int m = 0; m < 3; m++) {
if((k != 0 && k == i) || (m != 0 && m == j))
continue;
dp[l][r][i][j] = (dp[l][r][i][j] + dp[l + 1][r - 1][k][m]) % mod;
}
}
}
}
} else {
dfs(l, tail[l]);
dfs(tail[l] + 1, r);
for(int i = 0; i < 3; i++) {
for(int j = 0; j < 3; j++) {
if(i == 0 && j == 0 || (i != 0 && j != 0))
continue;
for(int k = 0; k < 3; k++) {
if(k != 0 && k == j)
continue;
for(int m = 0; m < 3; m++) {
dp[l][r][i][m] = (dp[l][r][i][m] + (dp[l][tail[l]][i][j] * dp[tail[l] + 1][r][k][m]) % mod) % mod;
}
}
}
}
}
}
int main() {
ios::sync_with_stdio(false);//優化
cin >> s;
ll n = s.size();
stack<ll> st;
//預處理
for(ll i = 0; i < s.size(); i++) {
if(s[i] == '(')
st.push(i);
else {
tail[st.top()] = i;
st.pop();
}
}
dfs(0, n - 1);
ll res = 0;
for(ll i = 0; i < 3; i++) {
for(ll j = 0; j < 3; j++) {
res = (res + dp[0][n - 1][i][j]) % mod;
}
}
cout << res << endl;
}