题目链接
https://codeforces.com/problemset/problem/149/D
题意
给出一个匹配完成的括号串,每个括号都可以染成a,b两色或是不染,要求:
- 每个匹配的括号需要有且仅一个染色
- 相邻括号不可染同色,可都不染
问染色方案数
思路
注意给我们的是一个已经匹配好的串,在这个串里,每一个括号的匹配对象是已经定好的。因此我们拿到一个区间,不是要像做合法括号匹配数那样去猜测匹配方案,而是要对固定好的方案进行染色处理。
对于区间l-r,有两种可能:
- l-r是一个匹配,那么这时他的方案数应该是l+1 r-1的方案数继承而来。
- l-r不是匹配,那么一定有且仅有一个位置k让rk匹配,l k-1匹配。那么这时方案数应该是两个区间各个情况乘积求和。
我们使用四维dp[l][r][i][j]。代表l到r区间左边染i右边染j情况,ij取0为无色。如果是上文所说方案一,那么(忽略一二维)就是0 1由j不是1的继承来,1 0由i不是1的继承来。。。依次类推,但注意lr匹配,dp[l][r][0][0]这种非法情况答案为0。
假如说是方案二,那么我们要枚举四个变量i1,j1,i2,j2。dp[l][r][i1][j2]由所有合法的dp[l][k-1][i1][j1]*dp[k][r][i2][j2]加和而来。这里注意我们说的合法是指j1和i2不能同时染同色。
再提一嘴方案二,既然我们确定k和r匹配,l和k-1匹配,那么是不是还能剔除掉一些无效方案,比如i2=j2=0?当然可以,但是只要我们初值赋的合适,答案正确性是不会错的,为了提高一点效率(甚至加一堆判断没准还提高不了)让代码乱七八糟的不太合算。
至于初值,我们线性遍历,让i与i+1匹配的情况下dp[i][i+1]的合法情况为1(1 0,2 0,0 1,0 2),其余都为0即可。
最终答案就是dp[1][n]的所有值求和,注意是所有值,因为1-n不一定匹配。
代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<chrono>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
#define int long long
//#define double long double
using namespace std;
typedef long long ll;
const int maxn=705;
const int inf=0x3f3f3f3f;
const int mod=1000000007;
int n,m,k;
int a[maxn];
int ans;
int dp[maxn][maxn][3][3];
string s;
int match[maxn];
inline int pls(int a,int b){
return (a+b)%mod;
}
inline int mul(int a,int b){
return (a*b)%mod;
}
signed main(){
IOS
#ifndef ONLINE_JUDGE
freopen("IO\\in.txt","r",stdin);
freopen("IO\\out.txt","w",stdout);
#endif
int tn=1;
cin>>s;
n=s.size();
s='0'+s;
stack<int>sa;
for(int i=1;i<=n;i++){
if(s[i]=='(') sa.push(i);
else{
match[i]=sa.top();
match[sa.top()]=i;
sa.pop();
}
}
for(int i=1;i<n;i++){
if(match[i]==i+1)
dp[i][i+1][0][1]=dp[i][i+1][0][2]=dp[i][i+1][1][0]=dp[i][i+1][2][0]=1;
}
for(int len=2;len<=n;len++){
for(int l=1;l+len-1<=n;l++){
int r=l+len-1;
if(match[l]==r){//是匹配的,那么答案就是所有合法构造
for(int t1=0;t1<3;t1++)
for(int t2=0;t2<3;t2++){
if(t2!=1) dp[l][r][0][1]=pls(dp[l][r][0][1],dp[l+1][r-1][t1][t2]);
if(t2!=2) dp[l][r][0][2]=pls(dp[l][r][0][2],dp[l+1][r-1][t1][t2]);
if(t1!=1) dp[l][r][1][0]=pls(dp[l][r][1][0],dp[l+1][r-1][t1][t2]);
if(t1!=2) dp[l][r][2][0]=pls(dp[l][r][2][0],dp[l+1][r-1][t1][t2]);
}
}
else{
int k=match[r];
for(int t1=0;t1<3;t1++)
for(int t2=0;t2<3;t2++)
for(int t3=0;t3<3;t3++)
for(int t4=0;t4<3;t4++){
if(!((t3==t4)&&t3))
dp[l][r][t1][t2]=pls(dp[l][r][t1][t2],mul(dp[l][k-1][t1][t3],dp[k][r][t4][t2]));
}
}
}
}
int ans=0;
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
ans=pls(ans,dp[1][n][i][j]);
cout<<ans%mod<<endl;
}