【动态规划专练-区间DP】CF149D(1900)
【动态规划专练-区间DP】CF149D(1900)
题目大意:
给出能够配对的括号序列,按以下要求给括号上色:
- 一对括号必有一个染色,一个无色
- 相邻的有色括号不能是相同的颜色
求上色方案数目,对1e9+7取模
思路:
由这一句
“ it is possible to obtain a correct mathematical expression from it, inserting numbers and operators between the brackets.”
我们知道,这里的括号序列必须是完全的包含或被包含的关系
所以可以预处理出匹配括号的位置:
int temp[1005],match[1005];string ss;
void getmatch(int len){
int c = 0;
for(int i=0;i<len;i++){
if(ss[i] == '(') temp[c++] = i;
else{
match[i] = temp[c-1];
match[temp[c-1]] = i;
c--;
}
}
}
区间DP,研究左右边界。
- 最后一步+子问题: 长的序列由短的序列得来,[l,r]若左右边界相等 则状态由[l+1,r-1]得来,不相等的话,则是[l,p] [p,r]的两个子区间转移得来。
- 因为相邻括号之间有限制,而区间dp的状态都是加在边界上的,所以设状态转移方程: \(f[l][r][i][j]\)表示区间[l,r]两端颜色分别是i,j的涂色方案数目
- 注意转移顺序:必须先计算小区间,再计算大区间
ll f[1005][1005][4][4];
int temp[1005],match[1005];string ss;
void getmatch(int len){
int c = 0;
for(int i=0;i<len;i++){
if(ss[i] == '(') temp[c++] = i;
else{
match[i] = temp[c-1];
match[temp[c-1]] = i;
c--;
}
}
}
void dfs(int l,int r){
if(l+1 == r){
f[l][r][0][1] = 1;
f[l][r][1][0] = 1;
f[l][r][0][2] = 1;
f[l][r][2][0] = 1;
return ;
}
if(match[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+1][r-1][i][j],f[l][r][0][1]%=mod;
if(i!=1)
f[l][r][1][0] += f[l+1][r-1][i][j],f[l][r][1][0]%=mod;
if(j!=2)
f[l][r][0][2] += f[l+1][r-1][i][j],f[l][r][0][2]%=mod;
if(i!=2)
f[l][r][2][0] += f[l+1][r-1][i][j],f[l][r][2][0]%=mod;
}
}
}
else{
int p = match[l];
dfs(l,p); dfs(p+1,r);
for(int i=0;i<3;i++){ //注意转移顺序,先计算小区间再大区间,所以四个边界 i k q j
for(int j=0;j<3;j++){ //i j 在最外层
for(int k=0;k<3;k++){
for(int q=0;q<3;q++){
if(!((k==1&&q==1)||(k==2&&q==2)))
f[l][r][i][j]=(f[l][r][i][j]+(f[l][p][i][k]*f[p+1][r][q][j])%mod)%mod;
}
}
}
}
}
}
int main(){
cin>>ss;
int len = ss.length();
getmatch(len);
dfs(0,len-1);
ll ans = 0;
for(int i = 0; i < 3; i ++){
for(int j = 0; j < 3; j ++){
ans = (ans + f[0][len-1][i][j])%mod;
}
}
cout << ans << endl;
return 0;
}