一份比较简洁的解答
详见注释
题目:点我
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<stack>
using namespace std;
typedef long long ll;
const int INF =0x3f3f3f3f;
const int maxn=700 ;
char s[maxn+5];//用于保存字符串
int n;
int match[maxn+5];//match[x]代表和s[x]相匹配的字符的位置。
ll dp[maxn+5][maxn+5][3][3];//dp[le][ri][lecolor][ricolor]
//代表区间[le,ri]且le-1位置颜色为lecolor,ri+1位置颜色为ricolor 情况下的涂色种数
//0代表没有涂色。1代表涂红色,2代表涂蓝色。
const ll mod=1000000007 ;
void getmatch()//数据结构之经典括号匹配问题
{
stack<int>st;
for(int i=1;i<=n;i++)
{
if(s[i]=='(') st.push(i);
else
{
int t=st.top();st.pop();
match[t]=i;
match[i]=t;
}
}
}
ll DP(int le,int ri,int resl,int resr)
{
if(~dp[le][ri][resl][resr]) return dp[le][ri][resl][resr];
ll & ans =dp[le][ri][resl][resr];
if(le>=ri) return ans=1;//不存在的区间,种数记为1。
ans=0;
int x=match[ri];
//每一个状态相应的决策根据最右边的s[ri]来考虑
for(int l=0;l<3;l++)//枚举match[ri]的颜色
{
for(int r=0;r<3;r++) if(l&&!r || !l&&r)//枚举ri的颜色,
{//一对括号的颜色,必须满足题目要求,一个涂色,一个不涂色
if( r&&r==resr ) continue;//如果右边涂有颜色,而且和ri+1的颜色相同,不符合
if(x==le&&l&&l==resl) continue;//如果左边le正好是match[ri]的位置,涂色,而且与le-1颜色相同,不符合
ans=(ans+DP(le,x-1,resl,l)*DP(x+1,ri-1,l,r) )%mod;
}
}
return ans;
//相对于大部分网上的代码,这份代码其实是比较简洁的。
//这个题目使用记忆化搜索比递推要方便的多!!!
}
int main()
{
while(~scanf("%s",s+1))
{
n=strlen(s+1);
getmatch();
memset(dp,-1,sizeof dp);
printf("%lld\n",DP(1,n,0,0));
}
return 0;
}