(CodeForces - 149D)Coloring Brackets(区间DP)

题目链接:https://codeforces.com/problemset/problem/149/D

题意:

给定一个字符串 s,是一个合法的括号序列。我们打算对这个括号序列进行染色,有以下要求:

  • 每个字符有三种情况:不染色,染成红色,染成蓝色
  • 每对匹配的括号,有且仅有一个字符被染色
  • 所有相邻的两个字符,不能染成同一种颜色

求满足要求的括号序列染色方案数,答案可能很大,输出答案对 109 + 7 取模的结果。

这道题是用区间DP来解决的,首先需要有dp[i][j]表示区间起始位置,我们再来看一下条件,对相邻的两个字符以及匹配的字符颜色有要求,所以我们必须要加上两维来表示区间两端点的颜色,总地来说就是dp[i][j][k][l]表示区间i到j且左端点染k号颜色右端点染l号颜色的情况数,其中0代表无色,1代表红色,2代表蓝色,下面我们来看下状态转移方程:

首先我们对于区间i到j的更新必须分两种情况,一种是i与j是匹配的,另一种就是i和j是不匹配的,所以这就要求我们必须先处理一下字符串,找出与每个字符匹配的字符的位置,当然这个很容易就可以用栈来求解

如果i和j是匹配的,那么与区间[i,j]相关的就是区间[i+1,j-1],因为i+1和j-1也是匹配的,所以我们只关心这个区间就可以了,要求无非就是第i+1个字符不能与第i个字符颜色相同(但是可以均为无色),第j-1个字符不能与第j个字符颜色相同,所以这个比较好处理,状态转移方程如下:

for(int k=0;k<=2;k++)
            for(int l=0;l<=2;l++)
            {
                if(l!=1) dp[i][j][0][1]=(dp[i][j][0][1]+dp[i+1][j-1][k][l])%mod;
                if(l!=2) dp[i][j][0][2]=(dp[i][j][0][2]+dp[i+1][j-1][k][l])%mod;
                if(k!=1) dp[i][j][1][0]=(dp[i][j][1][0]+dp[i+1][j-1][k][l])%mod;
                if(k!=2) dp[i][j][2][0]=(dp[i][j][2][0]+dp[i+1][j-1][k][l])%mod;
            }

 观察这个转移方程我们发现,如果区间长度为2且这两个字符相匹配的话我们需要单独处理,因为dp[i][j](i>j)无意义,很显然对于这种情况,我们直接把四种合法情况都置为1即可

下面我们来看一下i和j不匹配的时候,那么我们需要把这个区间分成两段,一段是[ i,match[i] ],另一段是[ match[i]+1,j ],我们只需要把这两个区间的合法情况相乘取和即可,同样需要注意的是区间端点颜色不能相同,状态转移方程如下:

else//match[i]!=j
        {
            int t=match[i];
            for(int k=0;k<=2;k++)
            for(int l=0;l<=2;l++)
            for(int p=0;p<=2;p++)
            for(int q=0;q<=2;q++)
            {
                if(p==q&&(p!=0)) continue;
                dp[i][j][k][l]=(dp[i][j][k][l]+dp[i][t][k][p]*dp[t+1][j][q][l])%mod;
            }
        }

 下面我们来说一下如何初始化,我们首先需要把区间长度为1的区间进行初始化,也就是

for(int i=1;i<=length;i++)
    for(int j=0;j<=2;j++)
    for(int k=0;k<=2;k++)
        if(j&k==0&&(j|k)) dp[i][i][j][k]=1;

只要合法均将其方案数置为1

最后我们来说一下答案,了解了dp数组的含义,这个就比较简单了,就是把dp[1][length][i][j](0<=i,j<=2)累加即可

下面附上代码:

 

#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
#include<map>
#include<cmath>
#include<queue>
using namespace std;
const int N=703,mod=1e9+7;
char s[N];
int S[N],tt;
int match[N];//match[i]记录与i相匹配的括号的位置 
long long dp[N][N][3][3];
void init(int len)
{
	for(int i=1;i<=len;i++)
	{
		if(s[i]=='(') S[++tt]=i;
		else
		{
			int t=S[tt--];
			match[i]=t;
			match[t]=i;
		}
	}
}
int main()
{
	scanf("%s",s+1);
	int length=strlen(s+1);
	init(length);
	for(int i=1;i<=length;i++)
	for(int j=0;j<=2;j++)
	for(int k=0;k<=2;k++)
		if(j&k==0&&(j|k)) dp[i][i][j][k]=1;
	for(int len=2;len<=length;len++)
	for(int i=1;i+len-1<=length;i++)
	{
		int j=i+len-1;
		if(match[i]==j)
		{
			if(len==2)
			{
				dp[i][j][0][1]=dp[i][j][0][2]=1;
				dp[i][j][1][0]=dp[i][j][2][0]=1;
				continue;
			}
			for(int k=0;k<=2;k++)
			for(int l=0;l<=2;l++)
			{
				if(l!=1) dp[i][j][0][1]=(dp[i][j][0][1]+dp[i+1][j-1][k][l])%mod;
				if(l!=2) dp[i][j][0][2]=(dp[i][j][0][2]+dp[i+1][j-1][k][l])%mod;
				if(k!=1) dp[i][j][1][0]=(dp[i][j][1][0]+dp[i+1][j-1][k][l])%mod;
				if(k!=2) dp[i][j][2][0]=(dp[i][j][2][0]+dp[i+1][j-1][k][l])%mod;
			}
		}
		else//match[i]!=j
		{
			int t=match[i];
			for(int k=0;k<=2;k++)
			for(int l=0;l<=2;l++)
			for(int p=0;p<=2;p++)
			for(int q=0;q<=2;q++)
			{
				if(p==q&&(p!=0)) continue;
				dp[i][j][k][l]=(dp[i][j][k][l]+dp[i][t][k][p]*dp[t+1][j][q][l])%mod;
			}
		}
	}
	long long ans=0;
	for(int i=0;i<=2;i++)
	for(int j=0;j<=2;j++)
		ans=(ans+dp[1][length][i][j])%mod;
	printf("%lld",ans);
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值