【题解 && 区间dp】 Coloring Brackets

题目传送门

题目描述:

在这里插入图片描述


Solution

我们考虑一对括号能够产生的贡献。
d p [ l ] [ r ] [ i ] [ j ] dp[l][r][i][j] dp[l][r][i][j]表示当前区间为 [ l , r ] [l,r] [l,r],第 l l l个括号的颜色为 i i i,第 r r r个括号的颜色为 j j j的方案数。( i , j ∈ [ 0 , 1 , 2 ] i,j\in [0,1,2] i,j[0,1,2])

我们设 m a t [ i ] mat[i] mat[i]表示第 i i i个括号匹配到的右括号的位置(其中第 i i i个括号为左括号),这里我们需要用一个栈去操作:
在这里插入图片描述

接着对于问题在 [ l , r ] [l,r] [l,r]的区间,我们需要分类讨论:
1、 l + 1 = = r l+1==r l+1==r,此时对应这种情况:
在这里插入图片描述
即只有一个合法括号对,这时候我们直接赋值:
在这里插入图片描述
注意,同一对括号有且只能有一种颜色,所以两种颜色都有的是无法更新的。

2、 m a t [ l ] = = r mat[l]==r mat[l]==r,此时对应这种情况:
在这里插入图片描述
(咳咳,中间的字符就不用管了)
这个时候,我们的答案是能够直接从 [ l + 1 , r − 1 ] [l+1,r-1] [l+1,r1]这段区间转移过来的,直接类加上答案即可。需要注意的细节就是相邻的不能重复:
在这里插入图片描述
3、 m a t [ l ] ! = r mat[l]!=r mat[l]!=r,即这种情况:在这里插入图片描述

这种情况的时候我们需要先把两边的区间递归求解,然后再来合并答案。

这个时候有点麻烦,因为有四个括号,我们需要用四重循环分别取枚举每个括号的颜色,同时需要注意将重复的给判掉。
由于两个括号序列是独立的,所以是相乘。
在这里插入图片描述

分类讨论的情况我们结束了,接下来就是累加答案。

分别枚举 1 号 和 n 号 括 号 的 颜 色 1号和n号括号的颜色 1n然后累加即可。
在这里插入图片描述
此时不判颜色也没关系,因为贡献就位0,当然判了也没关系。


Code

#include<bits/stdc++.h>
using namespace std;

#define int long long

const int N = 1e3+10,P = 1e9+7;
int n;
char s[N];
int dp[N][N][3][3];
int mat[N];

void Find(){
	stack < int > q;
	for (int i = 1; i <= n; i++)
	  if (s[i] == '(') q.push(i);
	  else mat[q.top()] = i,q.pop();
}
#define f dp[l][r]
#define f1 dp[l][mat[l]]
#define f2 dp[mat[l]+1][r]
#define f3 dp[l+1][r-1]
void dfs(int l,int r){
	if (l + 1 == r){
		f[0][1] = f[0][2] = f[1][0] = f[2][0] = 1;
		return;
	}
	if (mat[l] == r){
		dfs(l+1,r-1);
		for (int i = 0; i <= 2; i++)
		  for (int j = 0; j <= 2; j++){
		  	  if (i!=1) f[1][0] = (f[1][0] + f3[i][j])%P;
		  	  if (i!=2) f[2][0] = (f[2][0] + f3[i][j])%P;
		  	  if (j!=1) f[0][1] = (f[0][1] + f3[i][j])%P;
		  	  if (j!=2) f[0][2] = (f[0][2] + f3[i][j])%P;
		  }
		  return;
	}
	dfs(l,mat[l]); dfs(mat[l]+1,r);
	for (int il = 0; il <= 2; il++)
	  for (int jl = 0; jl <= 2; jl++)
	    for (int jr = 0; jr <= 2; jr++)
	      for (int ir = 0; ir <= 2; ir++){
	      	if ((jl == jr) && jl != 0) continue;
	      	  f[il][ir] = (f[il][ir] + f1[il][jl] * f2[jr][ir])%P;
	      }
	return;
	  
}

signed main(){
	scanf("%s",s+1);
	n = strlen(s+1);
	Find();
	dfs(1,n);
	int ans = 0;
	for (int i = 0; i <= 2; i++)
	  for (int j = 0; j <= 2; j++) ans = (ans + dp[1][n][i][j])%P;
	cout<<ans;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值