BZOJ 4922 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。

新加两组数据by alone_wolf 2017.6.20






首先应该去做一下bzoj3709
我们可以把所有的括号先处理一边,只留下不匹配的内容。
比如(())),其实相当于),
接着我们可以把(看作1,)看作-1,那么根据括号匹配的内容,
我们不停地累计一个值S,要满足:
1.过程中S>=0
2.结果S=0
对于每个处理后的串))...)(...((,
我们得出这个串的值是所有括号的值累加起来,
然后dp[i][j]表示前i个串,这个累计的值为j的最大长度。
转移就是个01背包……
不过要注意,对于当前值j,i这个串能取,前提是这个串的中途S不会小于0
比如说))(,那么前提就是j>=2,要把2个右括号都排除掉。
这个过程可以预处理出来的。

把处理过后的需要左括号的右括号数目看作攻击力,
然后总的左括号减去右括号看作恢复血量,
如何让S>0,这个贪心从bzoj3709里可以得到结论。

代码很丑……



#include<bits/stdc++.h>
using namespace std;
const int 
	N=305,
	M=90005;
int n,f[N][M];
char s[N];
bool flag[N];
struct node{
	int Len,Sum,x,y;
}a[N],ta[N],tb[N];

bool cmp1(node A,node B){return A.x<B.x;}
bool cmp2(node A,node B){return A.y>B.y;}

int Stack[N];
void get(int x){
	int i=1,top=0;
	while (i<=a[x].Len){
		flag[i]=0;
		if (s[i]=='(') flag[i]=1,Stack[++top]=i; else
		if (!top) flag[i]=1;
		 else flag[Stack[top--]]=0;
		i++;
	}
	a[x].x=0,a[x].Sum=0;
	for (i=1;i<=a[x].Len;i++)
		if (flag[i])
			if (s[i]=='(') a[x].Sum++; else
				a[x].Sum--,a[x].x=max(a[x].x,-a[x].Sum);
	a[x].y=a[x].x+a[x].Sum;
}
void getorder(){
	int cnt1=0,cnt2=0;
	for (int i=1;i<=n;i++)
		if (a[i].y-a[i].x>0) ta[++cnt1]=a[i];
			else tb[++cnt2]=a[i];
	sort(ta+1,ta+1+cnt1,cmp1);
	sort(tb+1,tb+1+cnt2,cmp2);

	n=0;
	for (int i=1;i<=cnt1;i++) a[++n]=ta[i];
	for (int i=1;i<=cnt2;i++) a[++n]=tb[i];
}
void DP(){
	memset(f,128,sizeof(f));
	f[0][0]=0;int tsum=0;
	for (int i=1;i<=n;i++){
		int now=i&1,pre=now^1;
		for (int j=a[i].x;j<=tsum;j++) f[now][j]=f[pre][j];
		for (int j=a[i].x;j<=tsum;j++)
			if (j+a[i].Sum>=0)
				f[now][j+a[i].Sum]=max(f[now][j+a[i].Sum],f[pre][j]+a[i].Len);
		if (a[i].Sum>0) tsum+=a[i].Sum;
	}
}
int main(){
	scanf("%d",&n);
	for (int i=1;i<=n;i++){
		scanf("%s",s+1);
		a[i].Len=strlen(s+1);
		get(i);
	}
	getorder();

	DP();
	printf("%d\n",f[n&1][0]>0?f[n&1][0]:0);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值