cf149d 区间DP

20 篇文章 0 订阅
题目链接

https://codeforces.com/problemset/problem/149/D

题意

给出一个匹配完成的括号串,每个括号都可以染成a,b两色或是不染,要求:

  • 每个匹配的括号需要有且仅一个染色
  • 相邻括号不可染同色,可都不染

问染色方案数

思路

注意给我们的是一个已经匹配好的串,在这个串里,每一个括号的匹配对象是已经定好的。因此我们拿到一个区间,不是要像做合法括号匹配数那样去猜测匹配方案,而是要对固定好的方案进行染色处理。

对于区间l-r,有两种可能:

  • l-r是一个匹配,那么这时他的方案数应该是l+1 r-1的方案数继承而来。
  • l-r不是匹配,那么一定有且仅有一个位置k让rk匹配,l k-1匹配。那么这时方案数应该是两个区间各个情况乘积求和。

我们使用四维dp[l][r][i][j]。代表l到r区间左边染i右边染j情况,ij取0为无色。如果是上文所说方案一,那么(忽略一二维)就是0 1由j不是1的继承来,1 0由i不是1的继承来。。。依次类推,但注意lr匹配,dp[l][r][0][0]这种非法情况答案为0。

假如说是方案二,那么我们要枚举四个变量i1,j1,i2,j2。dp[l][r][i1][j2]由所有合法的dp[l][k-1][i1][j1]*dp[k][r][i2][j2]加和而来。这里注意我们说的合法是指j1和i2不能同时染同色。

再提一嘴方案二,既然我们确定k和r匹配,l和k-1匹配,那么是不是还能剔除掉一些无效方案,比如i2=j2=0?当然可以,但是只要我们初值赋的合适,答案正确性是不会错的,为了提高一点效率(甚至加一堆判断没准还提高不了)让代码乱七八糟的不太合算。

至于初值,我们线性遍历,让i与i+1匹配的情况下dp[i][i+1]的合法情况为1(1 0,2 0,0 1,0 2),其余都为0即可。

最终答案就是dp[1][n]的所有值求和,注意是所有值,因为1-n不一定匹配。

代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib> 
#include<chrono>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
#define int long long
//#define double long double
using namespace std;
	typedef long long ll;
	const int maxn=705;
	const int inf=0x3f3f3f3f;
    const int mod=1000000007;
	int n,m,k;
    int a[maxn];
    int ans;
    int dp[maxn][maxn][3][3];
    string s;
    int match[maxn];
    inline int pls(int a,int b){
        return (a+b)%mod;
    }
    inline int mul(int a,int b){
        return (a*b)%mod;
    }
	signed main(){
        IOS

		#ifndef ONLINE_JUDGE
		    freopen("IO\\in.txt","r",stdin);
		    freopen("IO\\out.txt","w",stdout);
        #endif
		int tn=1;
        cin>>s;
        n=s.size();
        s='0'+s;
        stack<int>sa;
        for(int i=1;i<=n;i++){
            if(s[i]=='(')   sa.push(i);
            else{
                match[i]=sa.top();
                match[sa.top()]=i;
                sa.pop();
            }
        }
        for(int i=1;i<n;i++){
            if(match[i]==i+1)
                dp[i][i+1][0][1]=dp[i][i+1][0][2]=dp[i][i+1][1][0]=dp[i][i+1][2][0]=1;
        }
        for(int len=2;len<=n;len++){
            for(int l=1;l+len-1<=n;l++){
                int r=l+len-1;
                if(match[l]==r){//是匹配的,那么答案就是所有合法构造
                    for(int t1=0;t1<3;t1++)
                        for(int t2=0;t2<3;t2++){
                            if(t2!=1)   dp[l][r][0][1]=pls(dp[l][r][0][1],dp[l+1][r-1][t1][t2]);    
                            if(t2!=2)   dp[l][r][0][2]=pls(dp[l][r][0][2],dp[l+1][r-1][t1][t2]);    
                            if(t1!=1)   dp[l][r][1][0]=pls(dp[l][r][1][0],dp[l+1][r-1][t1][t2]);    
                            if(t1!=2)   dp[l][r][2][0]=pls(dp[l][r][2][0],dp[l+1][r-1][t1][t2]);    
                        }
                }
                else{
                    int k=match[r];
                    for(int t1=0;t1<3;t1++)
                        for(int t2=0;t2<3;t2++)
                            for(int t3=0;t3<3;t3++)
                                for(int t4=0;t4<3;t4++){
                                    if(!((t3==t4)&&t3))
                                        dp[l][r][t1][t2]=pls(dp[l][r][t1][t2],mul(dp[l][k-1][t1][t3],dp[k][r][t4][t2]));
                                }
                }
            }
        }
        int ans=0;
        for(int i=0;i<3;i++)
            for(int j=0;j<3;j++)
                ans=pls(ans,dp[1][n][i][j]);
        cout<<ans%mod<<endl;
	} 
						
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值