题目链接:https://codeforces.com/problemset/problem/149/D
题意:
给定一个字符串 s,是一个合法的括号序列。我们打算对这个括号序列进行染色,有以下要求:
- 每个字符有三种情况:不染色,染成红色,染成蓝色
- 每对匹配的括号,有且仅有一个字符被染色
- 所有相邻的两个字符,不能染成同一种颜色
求满足要求的括号序列染色方案数,答案可能很大,输出答案对 109 + 7 取模的结果。
这道题是用区间DP来解决的,首先需要有dp[i][j]表示区间起始位置,我们再来看一下条件,对相邻的两个字符以及匹配的字符颜色有要求,所以我们必须要加上两维来表示区间两端点的颜色,总地来说就是dp[i][j][k][l]表示区间i到j且左端点染k号颜色右端点染l号颜色的情况数,其中0代表无色,1代表红色,2代表蓝色,下面我们来看下状态转移方程:
首先我们对于区间i到j的更新必须分两种情况,一种是i与j是匹配的,另一种就是i和j是不匹配的,所以这就要求我们必须先处理一下字符串,找出与每个字符匹配的字符的位置,当然这个很容易就可以用栈来求解。
如果i和j是匹配的,那么与区间[i,j]相关的就是区间[i+1,j-1],因为i+1和j-1也是匹配的,所以我们只关心这个区间就可以了,要求无非就是第i+1个字符不能与第i个字符颜色相同(但是可以均为无色),第j-1个字符不能与第j个字符颜色相同,所以这个比较好处理,状态转移方程如下:
for(int k=0;k<=2;k++)
for(int l=0;l<=2;l++)
{
if(l!=1) dp[i][j][0][1]=(dp[i][j][0][1]+dp[i+1][j-1][k][l])%mod;
if(l!=2) dp[i][j][0][2]=(dp[i][j][0][2]+dp[i+1][j-1][k][l])%mod;
if(k!=1) dp[i][j][1][0]=(dp[i][j][1][0]+dp[i+1][j-1][k][l])%mod;
if(k!=2) dp[i][j][2][0]=(dp[i][j][2][0]+dp[i+1][j-1][k][l])%mod;
}
观察这个转移方程我们发现,如果区间长度为2且这两个字符相匹配的话我们需要单独处理,因为dp[i][j](i>j)无意义,很显然对于这种情况,我们直接把四种合法情况都置为1即可
下面我们来看一下i和j不匹配的时候,那么我们需要把这个区间分成两段,一段是[ i,match[i] ],另一段是[ match[i]+1,j ],我们只需要把这两个区间的合法情况相乘取和即可,同样需要注意的是区间端点颜色不能相同,状态转移方程如下:
else//match[i]!=j
{
int t=match[i];
for(int k=0;k<=2;k++)
for(int l=0;l<=2;l++)
for(int p=0;p<=2;p++)
for(int q=0;q<=2;q++)
{
if(p==q&&(p!=0)) continue;
dp[i][j][k][l]=(dp[i][j][k][l]+dp[i][t][k][p]*dp[t+1][j][q][l])%mod;
}
}
下面我们来说一下如何初始化,我们首先需要把区间长度为1的区间进行初始化,也就是
for(int i=1;i<=length;i++)
for(int j=0;j<=2;j++)
for(int k=0;k<=2;k++)
if(j&k==0&&(j|k)) dp[i][i][j][k]=1;
只要合法均将其方案数置为1
最后我们来说一下答案,了解了dp数组的含义,这个就比较简单了,就是把dp[1][length][i][j](0<=i,j<=2)累加即可
下面附上代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
#include<map>
#include<cmath>
#include<queue>
using namespace std;
const int N=703,mod=1e9+7;
char s[N];
int S[N],tt;
int match[N];//match[i]记录与i相匹配的括号的位置
long long dp[N][N][3][3];
void init(int len)
{
for(int i=1;i<=len;i++)
{
if(s[i]=='(') S[++tt]=i;
else
{
int t=S[tt--];
match[i]=t;
match[t]=i;
}
}
}
int main()
{
scanf("%s",s+1);
int length=strlen(s+1);
init(length);
for(int i=1;i<=length;i++)
for(int j=0;j<=2;j++)
for(int k=0;k<=2;k++)
if(j&k==0&&(j|k)) dp[i][i][j][k]=1;
for(int len=2;len<=length;len++)
for(int i=1;i+len-1<=length;i++)
{
int j=i+len-1;
if(match[i]==j)
{
if(len==2)
{
dp[i][j][0][1]=dp[i][j][0][2]=1;
dp[i][j][1][0]=dp[i][j][2][0]=1;
continue;
}
for(int k=0;k<=2;k++)
for(int l=0;l<=2;l++)
{
if(l!=1) dp[i][j][0][1]=(dp[i][j][0][1]+dp[i+1][j-1][k][l])%mod;
if(l!=2) dp[i][j][0][2]=(dp[i][j][0][2]+dp[i+1][j-1][k][l])%mod;
if(k!=1) dp[i][j][1][0]=(dp[i][j][1][0]+dp[i+1][j-1][k][l])%mod;
if(k!=2) dp[i][j][2][0]=(dp[i][j][2][0]+dp[i+1][j-1][k][l])%mod;
}
}
else//match[i]!=j
{
int t=match[i];
for(int k=0;k<=2;k++)
for(int l=0;l<=2;l++)
for(int p=0;p<=2;p++)
for(int q=0;q<=2;q++)
{
if(p==q&&(p!=0)) continue;
dp[i][j][k][l]=(dp[i][j][k][l]+dp[i][t][k][p]*dp[t+1][j][q][l])%mod;
}
}
}
long long ans=0;
for(int i=0;i<=2;i++)
for(int j=0;j<=2;j++)
ans=(ans+dp[1][length][i][j])%mod;
printf("%lld",ans);
return 0;
}