【BZOJ 4922】Karp-de-Chant Number

本文探讨了如何从多个括号序列中选择并拼接成一个合法括号序列,目标是最长长度。通过分析括号特性,采用排序与动态规划算法解决。关键在于合理排序以避免浪费左括号。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

现给定 n ( 1 ≤ n ≤ 300 ) n(1\le n\le 300) n(1n300) 个括号序列,你需要选择若干序列,将它们按一定的顺序从左往右拼接起来,得到一个合法的括号序列,计算可以得到的合法的括号序列的长度的最大值。括号序列仅由小括号组成且长度在 [ 1 , 300 ] [1,300] [1,300] 之间。

算法分析

据说是个套路题,思路来源于 【WF 2016】Swap Space。

如果我们将括号序列内的所有配对括号都删去的话,得到的括号序列一定是由前面若干个(可以为 0 0 0)右括号和后面若干个(可以为 0 0 0)左括号组成。

因此,我们可以以目前左括号的个数作为背包 DP 的容量,序列的长度作为背包 DP 的价值进行转移,然而这些括号序列是无序的……

考虑如何通过排序使得这些括号序列变得有序,进而可以使用背包 DP 计算,显然应当让消去后左边右括号个数比右边左括号个数小的更靠前,这样可以尽量少的出现左括号浪费的情况。

满足此前提后,对于左边右括号个数比右边左括号个数小的括号序列按照左括号个数从小到大排序,对于左边右括号个数比右边左括号个数大的括号序列按照右括号个数从大到小排序,然后直接 DP 即可。

参考:链接

代码实现

#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAXN 305
#define cmax(x,y) x=std::max(x,y)
struct type {
	int x,y,l;
	bool operator < (const type &rhs) const {
		if(x<=y&&rhs.x>rhs.y) return true;
		if(x>y&&rhs.x<=rhs.y) return false;
		if(x<=y&&rhs.x<=rhs.y) return x<rhs.x;
		return y>rhs.y;
	}
} arr[MAXN];
char s[MAXN];int f[MAXN*MAXN];
int main() {
	int n;scanf("%d",&n);
	int sum=0;
	for(int i=1;i<=n;++i) {
		scanf("%s",s);
		int x=0,y=0,l=strlen(s);
		for(int j=0;j<l;++j) {
			if(s[j]=='(') ++y;
			else y?--y:++x;
		}
		arr[i]=(type){x,y,l};
		sum+=y;
	}
	std::sort(arr+1,arr+1+n);
	memset(f,0xc0,sizeof(f));f[0]=0;
	for(int i=1;i<=n;++i) {
		if(arr[i].x>arr[i].y) for(int j=arr[i].x;j<=sum;++j) 
			cmax(f[j-arr[i].x+arr[i].y],f[j]+arr[i].l);
		else for(int j=sum;j>=arr[i].x;--j)
			cmax(f[j-arr[i].x+arr[i].y],f[j]+arr[i].l);
	}
	printf("%d\n",f[0]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值