牛客多校2 - Link with Bracket Sequence I(括号序列,三维DP)

https://ac.nowcoder.com/acm/contest/33187/K

题意
给定一个长度为 n n n 的括号序列 a a a,其是长度为 m m m 的合法括号序列 b b b 的一个子序列。
问,括号序列 b b b 一共有多少种?

1 ≤ T ≤ 100 ,   1 ≤ n ≤ m ≤ 200 ,   ∑ m ≤ 1 0 3 1≤T≤100,\ 1≤n≤m≤200,\ \sum m \leq10^3 1T100, 1nm200, m103

思路
如果不考虑长序列合不合法,只是问满足短序列为其子序列的长序列一共多少个,那么可以:
定义状态 f[i, j] 表示,短序列前 j 项是长序列前 i 项的子序列,满足的长序列的方案数。
状态转移,遍历长序列每个位置 i,遍历短序列每个位置 j

  • 当短序列第 j 个位置为 ( 时,如果长序列第 i 个位置为 ( 时,方案数 += f[i-1, j-1];如果第 i 个位置为 ) 时,方案数 += f[i-1, j]。
  • 当短序列第 j 个位置为 ) 时,如果长序列第 i 个位置为 ) 时,方案数 += f[i-1, j-1];如果第 i 个位置为 ( 时,方案数 += f[i-1, j]。

而现在除了要满足短序列为长序列的子序列之外,还要满足长序列时合法括号序列,所以需要多加一维,用于判断最终是否合法

定义状态 f[i, j, k] 表示,长序列的前 i 位,和短序列的前 j 位匹配 并且 左括号比右括号多 k 个的方案数。
那么最终的方案数就为 f[m, n, 0]

状态转移:
第一维枚举长序列的每一位 i,第二维枚举短序列的每一位 j,第三维枚举长序列中左括号比右括号多的个数 k

  • 如果 j=0,如果长序列 i 位置为 (f[i,j,k] += f[i-1, j, k-1];如果为 ) 时,f[i,j,k] += f[i-1, j, k+1]
  • 否则如果短序列 a[j] 为 ( 时,如果长序列 i 位置为 (f[i,j,k] += f[i-1, j-1, k-1];如果为 ) 时,f[i,j,k] += f[i-1, j, k+1]
  • 否则短序列 a[j] 为 ) ,如果长序列 i 位置为 )f[i,j,k] += f[i-1, j-1, k+1];如果为 ( 时,f[i,j,k] += f[i-1, j, k-1]

注意数组越界。

Code

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

#define Ios ios::sync_with_stdio(false),cin.tie(0)
#define int long long
#define PII pair<int,int>
#define pb push_back
#define fi first
#define se second
#define endl '\n'

const int N = 210, mod = 1e9+7;
int T, n, m;
char a[N];
int f[N][N][N];

void add(int &x, int y){
	x = (x + y) % mod;
}

signed main(){
	Ios;
	cin >> T;
	while(T--)
	{
		cin >> n >> m;
		cin >> a + 1;
		
		for(int i=0;i<=m;i++)
			for(int j=0;j<=min(n, i);j++)
				for(int k=0;k<=i;k++)
					f[i][j][k] = 0;
		
		f[0][0][0] = 1;
		for(int i=1;i<=m;i++)
		{
			for(int j=0;j<=min(n, i);j++)
			{
				for(int k=0;k<=i;k++)
				{
					if(j == 0)
					{
						if(k >= 1) add(f[i][j][k], f[i-1][j][k-1]);
						add(f[i][j][k], f[i-1][j][k+1]);
					}
					else if(a[j] == '(')
					{
						if(k >= 1) add(f[i][j][k], f[i-1][j-1][k-1]); // '('
						add(f[i][j][k], f[i-1][j][k+1]); // ')'
					}
					else
					{
						add(f[i][j][k], f[i-1][j-1][k+1]); // ')'
						if(k >= 1) add(f[i][j][k], f[i-1][j][k-1]);
					}
				}
			}
		}
		cout << f[m][n][0] << endl;
	}
	
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值