区间dp- CodeForces - 149D-给括号涂颜色

题目链接:https://vjudge.net/contest/308310#problem/D
题目大意:
题意:有一个字符串 s. 这个字符串是一个完全匹配的括号序列.在这个完全匹配的括号序列里,每个括号都有一个和它匹配的括号
你现在可以给这个匹配的括号序列中的括号染色,且有三个要求:
每个括号只有三种情况,不上色,上红色,上蓝色.
每对括号必须只能给其中的一个上色,且必须给一个上色
相邻的两个不能上同色,可以都不上色
求满足条件的括号序列染色的方法数
在这里插入图片描述

思路:这里有个条件:匹配的括号必须只涂一个,匹配括号位置可能是一个区间,所以只能是区间dp了,用dp[L][R][u][v]表示区间L, R在括号s[L]涂u色,s[R]涂v色的涂色方案。

假设不染色为0,另外两种色为1,2。
那对于长度为2的一个匹配的括号对来说,只允许(1,0),(2,0),(0,1),(0,2)。
初始化:

if(R-L==1)
{
    dp[L][R][1][0]=1, dp[L][R][2][0]=1;
    dp[L][R][0][1]=1, dp[L][R][0][2]=1;
    return 0;
}

因为对匹配的括号涂色有限制。
对于dp[L][R][u][v]分两种情况:
1:L和R匹配
在这里插入图片描述

for(int u=0;u<3;u++)
{
    for(int v=0;v<3;v++)
    {
        for(int x=0;x<3;x++)
        {
            for(int y=0;y<3;y++)
            {
                if((u==0||v==0)&&(u!=0||v!=0)&&(u!=x||u+x==0)&&(v!=y||v+y==0))
                {
                    dp[L][R][u][v]+=dp[L+1][R-1][x][y], dp[L][R][u][v]%=mod;
                }
            }
        }
    }
}

或许有人会问为什么不考虑L+1和R-1的匹配情况。如果匹配的话那么L+1 和 R - 1只能涂一种颜色。我们没有考虑是因为,从初始化开始,如果匹配,那么颜色相同的方案数=0,不影响结果。

1:L和R不匹配
在这里插入图片描述

dfs(L, mp[L]); dfs(mp[L]+1, R);
for(int u=0;u<3;u++)
{
    for(int v=0;v<3;v++)
    {
        for(int x=0;x<3;x++)
        {
            for(int y=0;y<3;y++)
            {
                if((u==0||v==0)&&(u!=0||v!=0)&&(v!=x||v==0&&x==0))
                {
                    dp[L][R][u][y]+=dp[L][mp[L]][u][v]*dp[mp[L]+1][R][x][y], dp[L][R][u][y]%=mod;
                }
            }
        }
    }
}

同样的,我们不用去考虑子状态的匹配情况,如果这种情况不存在,那么方案数=0, 不影响结果。

思考:状态转移这这考虑确定的情况,例如这两个括号一定匹配,一定相邻,那么不合法情况不转移,方案数=0,这样向上转移时,就不用考虑子状态是否合法了。

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int mod=1000000007;

char s[705];
int q[705];
int mp[705]={0};
int tot=1;
LL dp[705][705][3][3]={0};
int vis[705][705]={0};
int dfs(int L, int R)
{
    if(vis[L][R])
    {
        return 0;
    }
    vis[L][R]=1;
    if(R-L==1)
    {
        dp[L][R][1][0]=1, dp[L][R][2][0]=1;
        dp[L][R][0][1]=1, dp[L][R][0][2]=1;
        return 0;
    }
    if(mp[L]==R)
    {
        dfs(L+1, R-1);
        for(int u=0;u<3;u++)
        {
            for(int v=0;v<3;v++)
            {
                for(int x=0;x<3;x++)
                {
                    for(int y=0;y<3;y++)
                    {
                        if((u==0||v==0)&&(u!=0||v!=0)&&(u!=x||u+x==0)&&(v!=y||v+y==0))
                        {
                            dp[L][R][u][v]+=dp[L+1][R-1][x][y], dp[L][R][u][v]%=mod;
                        }
                    }
                }
            }
        }
    }
    else
    {
        dfs(L, mp[L]); dfs(mp[L]+1, R);
        for(int u=0;u<3;u++)
        {
            for(int v=0;v<3;v++)
            {
                for(int x=0;x<3;x++)
                {
                    for(int y=0;y<3;y++)
                    {
                        if((u==0||v==0)&&(u!=0||v!=0)&&(v!=x||v==0&&x==0))
                        {
                            dp[L][R][u][y]+=dp[L][mp[L]][u][v]*dp[mp[L]+1][R][x][y], dp[L][R][u][y]%=mod;
                        }
                    }
                }
            }
        }
    }

}

int main()
{
    scanf("%s",s+1);
    int n=strlen(s+1);
    for(int i=1;i<=n;i++)
    {
        if(s[i]=='(')
        {
            q[tot++]=i;
        }
        else
        {
            mp[q[--tot]]=i;
        }
    }
    dfs(1, n);
    int ans=0;
    for(int u=0;u<3;u++)
    {
        for(int v=0;v<3;v++)
        {
            ans+=dp[1][n][u][v], ans%=1000000007;
        }
    }
    cout<<ans<<endl;

    return 0;
}


  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值