区间dp(CF 149D Coloring Brackets)

题意:给一个给定括号序列,给该括号上色,上色有三个要求
1、只有三种上色方案,不上色,上红色,上蓝色
2、每对括号必须只能给其中的一个上色
3、相邻的两个不能上同色,可以都不上色
求0-len-1这一区间内有多少种上色方案;
思路:区间dp,dp[l][r][i][j]代表区间l~r(l图i颜色,r图j颜色)(0代表无色,1代表蓝色,2代表红色);

if(l+1==r) 说明就只有一对那么
dp[l][r][0][1]=1;
dp[l][r][1][0]=1;
dp[l][r][0][2]=1;
dp[l][r][2][0]=1;

if(l和r匹配)
往中间递归.dfs(l+1,r-1);
dp[l][r][0][1]=(dp[l][r][0][1]+dp[l+1][r-1][i][j])%mod;
dp[l][r][1][0]=(dp[l][r][1][0]+dp[l+1][r-1][i][j])%mod;
dp[l][r][0][2]=(dp[l][r][0][2]+dp[l+1][r-1][i][j])%mod;
dp[l][r][2][0]=(dp[l][r][2][0]+dp[l+1][r-1][i][j])%mod;
要注意相邻的两个颜色不能相同(可以同时无色);
else
找与之匹配的点(dfs(l,与左边匹配的点),dfs(与左边匹配的点+1,r))
dp[l][r][i][j]=(dp[l][r][i][j]+(dp[l][p][i][k]*dp[p+1][r][q][j])%mod)%mod;

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int maxn=1e6+10;
const int mod=1e9+7;
using namespace std;
long long p[maxn],tmp[maxn],dp[710][710][3][3];
void getans(string s)//预处理括号之间的匹配
{
    int cont=0;
    int len=s.size();
    for(int i=0; i<len; i++)
    {
        if(s[i]=='(')
            tmp[cont++]=i;
        else
        {
            p[i]=tmp[--cont];
            p[tmp[cont]]=i;
        }
    }
}
void dfs(int l,int r)
{
    if(l+1==r)
    {
        dp[l][r][0][1]=1;
        dp[l][r][1][0]=1;
        dp[l][r][0][2]=1;
        dp[l][r][2][0]=1;
        return ;
    }
    if(p[l]==r)
    {
        dfs(l+1,r-1);
        for(int i=0; i<3; i++)
            for(int j=0; j<3; j++)
            {
                if(j!=1)
                    dp[l][r][0][1]=(dp[l+1][r-1][i][j]+dp[l][r][0][1])%mod;
                if(i!=1)
                    dp[l][r][1][0]=(dp[l+1][r-1][i][j]+dp[l][r][1][0])%mod;
                if(j!=2)
                    dp[l][r][0][2]=(dp[l+1][r-1][i][j]+dp[l][r][0][2])%mod;
                if(i!=1)
                    dp[l][r][2][0]=(dp[l+1][r-1][i][j]+dp[l][r][2][0])%mod;
            }
            return ;
    }
    else
    {
        int tmp=p[l];
        dfs(l,tmp),dfs(tmp+1,r);
        for(int i=0; i<3; i++)
            for(int j=0; j<3; j++)
                for(int k=0; k<3; k++)
                    for(int o=0; o<3; o++)
                    {
                        if(k==0||o==0||k!=o)
                        dp[l][r][i][j]=(dp[l][r][i][j]+(dp[l][tmp][i][k]*dp[tmp+1][r][o][j])%mod)%mod;
                    }
    }
}
int main()
{
    string s;
    while(cin>>s)
    {
        getans(s);
        memset(dp,0,sizeof(dp));
        int len=s.size();
        dfs(0,len-1);
        long long ans=0;
        for(int i=0;i<3;i++)
            for(int j=0;j<3;j++)
            ans=(dp[0][len-1][i][j]+ans)%mod;
        printf("%lld\n",ans);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值