Description
卡常数被称为计算机算法竞赛之中最神奇的一类数字,主要特点集中于令人捉摸不透,有时候会让水平很高的选手迷之超时。
普遍认为卡常数是埃及人Qa’a及后人发现的常数。也可认为是卡普雷卡尔(Kaprekar)常数的别称。主要用于求解括号序列问题。
据考证,卡(Qa’a)是古埃及第一王朝的最后一位法老。他发现并研究了一种常数,后世以他的名字叫做卡常数。卡特兰数的起源也是因为卡的后人与特兰克斯结婚,生下来的孩子就叫卡特兰,而他只是发表了祖传的家书而已。Sereja也是卡的后人,提出括号序列问题,也是从家书里得到的资料。然而Sereja为了不让这个秘密公开,于是隐瞒了这道题的真正做法。可是由于卡的后人不是各个都像卡特兰一样爱慕虚荣,这一算法也无法找到。“欲见贤人而不以其道,犹欲其入而闭之门也”。卡之常数的奥秘,需要以一颗诚心去追寻。
现给定n个括号序列,你需要选择若干序列,将它们按一定的顺序从左往右拼接起来,得到一个合法的括号序列。
显然,这个问题可以用卡常数解决,为了检验你是否会卡常数,请写一个程序,计算可以得到的合法的括号序列的长度的最大值。
Solution
一开始一直在想怎么确保序列的中途前缀和不会小于0,后来发现我们只要先选正的再选负数就可以了。
一个合法的括号序列显然满足每一位的前缀和不小于0。考虑求出每段括号序的和y,以及前缀和的最小值x。
我们把y不小于0的放前面,其余放后面。对于前半部分以x为关键字升序排序,后半部分以x+y为关键字降序排序
考虑这样做的正确性。
由于要使序列最长,那么肯定是先把使前缀和变大的选掉,这一部分能选就选
对于使前缀和变小的那些序列,我们优先选取剩余前缀和最大的那些,这样一定不会更劣
然后做一个背包就可以了。
Code
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define fill(x,t) memset(x,t,sizeof(x))
const int N=305;
struct data {
int x,y,len;
} a[N],b[N],d[N];
char str[N];
int f[N][N*N];
int read() {
int x=0,v=1; char ch=getchar();
for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
return x*v;
}
bool cmp1(data a,data b) {
return a.x<b.x;
}
bool cmp2(data a,data b) {
return a.x+a.y>b.x+b.y;
}
int main(void) {
int n=read();
rep(i,1,n) {
scanf("%s",str+1); d[i].len=strlen(str+1);
for (int j=1;j<=d[i].len;++j) {
d[i].y+=(str[j]=='(')?1:-1;
d[i].x=std:: max(d[i].x,-d[i].y);
}
}
int cntA=0,cntB=0;
rep(i,1,n) (d[i].y>=0)?(a[++cntA]=d[i]):(b[++cntB]=d[i]);
if (cntA) std:: sort(a+1,a+cntA+1,cmp1);
if (cntB) std:: sort(b+1,b+cntB+1,cmp2);
rep(i,1,cntA) d[i]=a[i];
rep(i,1,cntB) d[i+cntA]=b[i];
fill(f,-31); f[0][0]=0;
int sum=0;
rep(i,1,n) {
rep(j,0,sum) f[i][j]=f[i-1][j];
rep(j,d[i].x,sum) {
f[i][j+d[i].y]=std:: max(f[i][j+d[i].y],f[i-1][j]+d[i].len);
}
sum+=d[i].len;
}
printf("%d\n", f[n][0]);
return 0;
}