【BZOJ4922】[Lydsy六月月赛]Karp-de-Chant Number 贪心+动态规划

【BZOJ4922】[Lydsy六月月赛]Karp-de-Chant Number

Description

卡常数被称为计算机算法竞赛之中最神奇的一类数字,主要特点集中于令人捉摸不透,有时候会让水平很高的选手迷之超时。
普遍认为卡常数是埃及人Qa'a及后人发现的常数。也可认为是卡普雷卡尔(Kaprekar)常数的别称。主要用于求解括号序列问题。
据考证,卡(Qa'a)是古埃及第一王朝的最后一位法老。他发现并研究了一种常数,后世以他的名字叫做卡常数。卡特兰数的起源也是因为卡的后人与特兰克斯结婚,生下来的孩子就叫卡特兰,而他只是发表了祖传的家书而已。Sereja也是卡的后人,提出括号序列问题,也是从家书里得到的资料。然而Sereja为了不让这个秘密公开,于是隐瞒了这道题的真正做法。可是由于卡的后人不是各个都像卡特兰一样爱慕虚荣,这一算法也无法找到。“欲见贤人而不以其道,犹欲其入而闭之门也”。卡之常数的奥秘,需要以一颗诚心去追寻。
现给定n个括号序列,你需要选择若干序列,将它们按一定的顺序从左往右拼接起来,得到一个合法的括号序列。
显然,这个问题可以用卡常数解决,为了检验你是否会卡常数,请写一个程序,计算可以得到的合法的括号序列的长度的最大值。

Input

第一行包含一个正整数n(1<=n<=300),表示括号序列的个数。
接下来n行,每行一个长度在[1,300]之间的括号序列,仅由小括号构成。

Output

输出一行一个整数,即最大长度,注意你可以一个序列也不选,此时长度为0。

Sample Input

3
())
((()
)()

Sample Output

10

HINT

按{2,1,3}的顺序拼接得到((()()))(),总长度为10。

题解:先用栈求出每个串左边有多少多余的右括号l,右边有多少多余的左括号r。那么我们最终的序列一定是先来一些l<r的,再来一些r<l的。用f[i]表示右面还剩i个多余的左括号时,总长度的最大值,转移时显然是背包,但是转移顺序呢?这就是一个经典的贪心模型了。

对于l<r的,显然我们要先选择l更小的,因为这样可以获得更多的左括号来填掉多余的右括号;对于l>r的就反过来想,先选r更大的。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n,m;
int f[90010];
struct node
{
	int l,r,v;
}p[310];
char str[310];
bool cmp(const node &a,const node &b)
{
	if((a.r>a.l)!=(b.r>b.l))	return (a.r>a.l)>(b.r>b.l);
	if(a.r>a.l)	return a.l<b.l;
	else	return a.r>b.r;
}
int main()
{
	scanf("%d",&n);
	int i,j;
	for(i=1;i<=n;i++)
	{
		scanf("%s",str),p[i].v=strlen(str),m+=p[i].v;
		int top=0;
		for(j=0;j<p[i].v;j++)
		{
			if(str[j]==')')
			{
				if(top)	top--;
				else	p[i].l++;
			}
			else	top++;
		}
		for(top=0,j=p[i].v-1;j>=0;j--)
		{
			if(str[j]=='(')
			{
				if(top)	top--;
				else	p[i].r++;
			}
			else	top++;
		}
	}
	sort(p+1,p+n+1,cmp);
	memset(f,0xc0,sizeof(f));
	f[0]=0;
	for(i=1;i<=n;i++)
	{
		if(p[i].r>p[i].l)
		{
			for(j=m;j>=p[i].r;j--)	f[j]=max(f[j],f[j+p[i].l-p[i].r]+p[i].v);
		}
		else	for(j=p[i].r;j-p[i].r+p[i].l<=m;j++)	f[j]=max(f[j],f[j-p[i].r+p[i].l]+p[i].v);
	}
	printf("%d",f[0]);
	return 0;
}
原文地址:https://www.cnblogs.com/CQzhangyu/p/7859642.html