codeforces 149D Coloring Brackets 区间DP

15 篇文章 0 订阅

题意:给你一串括号,每个括号可以涂色,蓝色或者红色或者不涂,问你有多少种方案数,其中涂色有些限制。
1.每对括号有且仅有其中一个被涂色。
2.相邻的括号不能涂相同的颜色,但是相邻的括号可以同时不涂色。

那我们怎么做呢。
首先他已经给出了一个合法的括号序列,我们只要找每个左括号右边的第一个右括号就可以了,设dp[l][r][x][y]表示l到r这段区间的答案,l和r的颜色分别为x和y的方案数(0/1/2)。
那么dp有三种转移。
第一种l+1=r时。其实和初始化差不多。
dp[l][r][2][0]=1;
dp[l][r][0][2]=1;
dp[l][r][1][0]=1;
dp[l][r][0][1]=1;
相邻的括号不能都涂色。

第二种,r和l是一对匹配的括号。
这种情况下我们要计算l和r区间内合法的方案,简单来说就是括号套括号。转移如下:
dfs(l+1,r-1);
fo(i,0,2)
fo(j,0,2)
{
if (j!=1)
dp[l][r][0][1]=(dp[l][r][0][1]+dp[l+1][r-1][i][j])%mo;
if (i!=1)
dp[l][r][1][0]=(dp[l][r][1][0]+dp[l+1][r-1][i][j])%mo;
if (j!=2)
dp[l][r][0][2]=(dp[l][r][0][2]+dp[l+1][r-1][i][j])%mo;
if (i!=2)
dp[l][r][2][0]=(dp[l][r][2][0]+dp[l+1][r-1][i][j])%mo;
}
这个自己推一推都能知道。
第三种:普通情况,l和r没什么关系,这种情况其实就是矩阵连乘。。。
设mid表示和l匹配的那个括号。
dp[l][r][i][j]+=dp[l][mid][i][x]*dp[mid+1][r][y][j];

然后整个dp过程是用搜索完成的。。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int N=1e3;
const int mo=1e9+7;
const int inf=1e9;
int dp[N][N][3][3],a[N],match[N],b[N];
    char s[N];
int tot,len;
inline void get()
{
    int tot=0;
    fo(i,0,len-1)
    {
        if (s[i]=='(')b[tot++]=i;
        else 
        {
            match[i]=b[tot-1];
            match[b[tot-1]]=i;
            tot--;
        }
    }
}
inline void dfs(int l,int r)
{
    if (l+1==r)
    {
        dp[l][r][2][0]=1;
        dp[l][r][0][2]=1;
        dp[l][r][1][0]=1;
        dp[l][r][0][1]=1;
        return;
    }
    else if (match[l]==r)
    {
        dfs(l+1,r-1);
        fo(i,0,2)
            fo(j,0,2)
            {
                if (j!=1)
                dp[l][r][0][1]=(dp[l][r][0][1]+dp[l+1][r-1][i][j])%mo;
                if (i!=1)
                dp[l][r][1][0]=(dp[l][r][1][0]+dp[l+1][r-1][i][j])%mo;
                if (j!=2)
                dp[l][r][0][2]=(dp[l][r][0][2]+dp[l+1][r-1][i][j])%mo;
                if (i!=2)
                dp[l][r][2][0]=(dp[l][r][2][0]+dp[l+1][r-1][i][j])%mo;
            }
            return;
    }
    else 
    {
        int nu=match[l];
        dfs(l,nu);
        dfs(nu+1,r);
        fo(i,0,2)
        {
            fo(j,0,2)
            {
                fo(x,0,2)
                {
                    fo(y,0,2)
                    {
                        if (!(x==1&&y==1 || x==2&&y==2))
                        dp[l][r][i][j]=(dp[l][r][i][j]+dp[l][nu][i][x]*dp[nu+1][r][y][j])%mo;
                    }
                }
            }
        }
    }
}
int main()
{
    while(scanf("%s",s)!=EOF)
    {
        len=strlen(s);
        memset(dp,0,sizeof(dp));
        get();
        dfs(0,len-1);
        ll ans=0;
        fo(i,0,2)
        fo(j,0,2)
        ans=(ans+dp[0][len-1][i][j])%mo;
        printf("%lld\n",ans);
    }

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值