Coloring Brackets CodeForces - 149D

思想:dp的定义为dp[l][r][lc][rc],表示此区间一定为正确的括号匹配,从l到r区间,l位置上色为lc,r位置上色为rc的上色总方案数。

这道题与传统的区间DP不同,传统的区间DP枚举的是区间的长度,找断点。而对于这道题来说,需要找的是符合一个正确括号匹配的小区间来得到大区间的值,要排除不符合要求的小区间情况。通过观察得知,区间的合并不可能是相交的,只可能是包含与并列的关系。

这个博客的图画的非常好:Codeforces149 D. Coloring Brackets(区间dp,合法括号序列性质)_live4m的博客-优快云博客

此外,这道题的区间定义与传统不同,多定义了2个状态。通过上述分析,我们大致能够知道一个括号一定有唯一匹配,于是可以用栈找到相应的匹配。

DP的转移方程,首先,如果l,r相互匹配,那么就分析l+1,r-1的区间上色情况。

其次,如果不匹配,那么l的匹配点一定在l到r区间内,因为没有交叉的情况发生。

还应注意数据要用long long,情况一定要分析清楚。

这道题给人启发,让我复习了递归的DP写法,是一道非常好的题。

#include<bits/stdc++.h>
#include<cstring>
#include<string>
#include<iostream>
using namespace std;
const int volume=710;
const long long  mod=1e9+7;
long long dp[volume][volume][3][3];
stack<int> stk;
int match[volume];
int dfs(int l,int r,int lc,int rc)
{
    //cout<<l<<' '<<r<<' '<<lc<<' '<<rc<<endl;
    if(dp[l][r][lc][rc]!=0)
        return dp[l][r][lc][rc];
     if(l+1==r)
     {
        dp[l][r][lc][rc]=1;
        return 1;
     }
     if(match[l]==r)
        for(int i=0;i<3;i++)
        for(int j=0;j<3;j++)
    {
         if(i>0&&i==lc||j>0&&j==rc)continue;///相邻上色的不能颜色相同
         if(match[l+1]==r-1)
         {
         if(!i&&!j)continue;
         if(i>0&&j>0)continue;///匹配的恰有一个上色
         dp[l][r][lc][rc]+=dfs(l+1,r-1,i,j);
         dp[l][r][lc][rc]%=mod;
         }
         else
         {
         dp[l][r][lc][rc]+=dfs(l+1,r-1,i,j);
         dp[l][r][lc][rc]%=mod;
         }
    }
    else
    {
        int k=match[l];
        for(int x=0;x<3;x++)
            for(int y=0;y<3;y++)
              {
                if(!lc&&!x)continue;
                if(lc>0&&x>0)continue;
                if(x>0&&x==y)continue;///合并点相邻的不能上相同颜色
                if(match[k+1]==r)
                {
                 if(!rc&&!y)continue;
                if(rc>0&&y>0)continue;
                }
                dp[l][r][lc][rc]+=dfs(l,k,lc,x)%mod*dfs(k+1,r,y,rc)%mod;
                dp[l][r][lc][rc]%=mod;
              }
    }
    return dp[l][r][lc][rc];
}
int main()
{
    ///开启加速后,就不能cin,cout,scanf等混用
    ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	memset(dp,0,sizeof(dp));
    string s;
    cin>>s;
    int n=s.length();
    for(int i=0;i<n;i++)
    {
        if(s[i]=='(') stk.push(i);
        else
        match[stk.top()]=i,stk.pop();
    }
    int ans=0;
    for(int i=0;i<3;i++)
        for(int j=0;j<3;j++)
      {
          if(match[0]==n-1)
          {
          if(!i&&!j)continue;
          if(i>0&&j>0)continue;
          }
      ans+=dfs(0,n-1,i,j);
       ans%=mod;
      }
      ans%=mod;
      cout<<ans<<endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值