DP - 區間DP - Coloring Brackets - CodeForces - 149D
題意:
給定一個僅由"()"構成的括號序列s(保證整個序列能成功匹配),現給括號上色,需滿足:①、每個括號要麼不上色,要麼紅色,要麼藍色。②、一對匹配的括號僅有一個能夠被上色。③、相鄰的括號顏色不能相同,但可以都不上色。
要求整個序列上色的方案總數,答案對109+7取模。
Examples
Input:
(())
Output:
12
Input:
(()())
Output:
40
Input:
()
Output:
4
數據範圍:
2<=∣s∣<=700Time limit:2000ms,Memory limit:262144kB
分析:
與經典的括號序列匹配問題類似——《Brackets - POJ - 2955》,一個序列的染色方案數量的計算是相互獨立的。
狀態表示:f[l][r][a][b]表示區間[l,r]內,l被染上顏色a,r被染上顏色b的方案總數,a、b=0,1,2。0:不上色,1:紅色,2:藍色。
狀態計算:整個區間的方案總數,具體分爲兩種情況:
①、當s[l]與s[r]能夠匹配,具體又可分爲四種情況:
Ⅰ、a=1時,s[l+1]不能染相同的紅色,s[r]不能染色,故f[l][r][1][0]+=f[l+1][r−1][i][j],i=1。
Ⅱ、a=2時,s[l+1]不能染相同的藍色,s[r]不能染色,故f[l][r][2][0]+=f[l+1][r−1][i][j],i=2。
Ⅲ、b=1時,s[r−1]不能染相同的紅色,s[l]不能染色,故f[l][r][0][1]+=f[l+1][r−1][i][j],j=1。
Ⅳ、b=2時,s[r−1]不能染相同的藍色,s[l]不能染色,故f[l][r][0][2]+=f[l+1][r−1][i][j],j=2。
②、s[l]與s[r]不匹配,由乘法原理,區間[l,r]的方案總數f[l,r]可以分爲[l,k]的方案總數與區間[k+1,r]的方案總數之積。f[l][r][a][b]+=f[l][k][a][i]×f[k+1][r][j][b],k是與l匹配的括號下標。a,b=0,1,2且i=j或者i=j=0。
具體落實:
①、初始化邊界:當區間長度爲2時,合法方案數都爲1。
即:f[l][r][0][1]=1f[l][r][0][2]=1;f[l][r][1][0]=1;f[l][r][2][0]=1;
②、爲了簡化代碼,先將括號的匹配條件映射到下標上來。若s[l]與s[r]匹配,將下標l映射到r。
③、每個區間我們僅需計算一次,可以數組標記一下被搜索過的區間。
④、數組f的值可能達到109,乘積會達到1018,因此用ll類型來存儲。
代碼:
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<stack>
#define ll long long
using namespace std;
const int N=710,mod=1e9+7;
int mp[N],n;
ll f[N][N][3][3];
char s[N];
bool vis[N][N];
void trans()
{
stack<int> S;
for(int i=0;i<n;i++)
if(s[i]=='(')
S.push(i);
else
{
int tmp=S.top();
S.pop();
mp[tmp]=i;
mp[i]=tmp;
}
}
void dfs(int l,int r)
{
if(vis[l][r]) return ;
vis[l][r]=true;
if(l+1==r)
{
f[l][r][0][1]=f[l][r][1][0]=f[l][r][0][2]=f[l][r][2][0]=1;
return ;
}
if(mp[l]==r)
{
dfs(l+1,r-1);
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
{
if(j!=1) f[l][r][0][1]=(f[l][r][0][1]+f[l+1][r-1][i][j])%mod;
if(j!=2) f[l][r][0][2]=(f[l][r][0][2]+f[l+1][r-1][i][j])%mod;
if(i!=1) f[l][r][1][0]=(f[l][r][1][0]+f[l+1][r-1][i][j])%mod;
if(i!=2) f[l][r][2][0]=(f[l][r][2][0]+f[l+1][r-1][i][j])%mod;
}
}
else
{
int k=mp[l];
dfs(l,k);
dfs(k+1,r);
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
for(int a=0;a<3;a++)
for(int b=0;b<3;b++)
if( (a==0&&b==0) || a!=b)
f[l][r][i][j]=(f[l][r][i][j]+f[l][k][i][a]*f[k+1][r][b][j]%mod)%mod;
}
}
int main()
{
scanf("%s",s);
n=strlen(s);
trans();
dfs(0,n-1);
int res=0;
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
res=(res+f[0][n-1][i][j])%mod;
cout<<res<<endl;
return 0;
}