BZOJ4922 Karp-de-Chant Number(贪心+动态规划)
首先将每个括号序列转化为三元组(ai,bi,ci),其中ai为左括号-右括号数量,bi为前缀最小左括号-右括号数,ci为序列长度。问题变为在满足Σai=0,bi+Σaj>=0 (j<i)的情况下,最大化Σci。
考虑在确定了选哪些序列的情况下如何排列能够尽量满足条件。显然应该把ai>0的放在前面,<0的放在后面。对于ai>=0,考虑按bi降序排列。因为假设这样排列后第一个不合法的位置是x,要让该位置合法显然应该将x后面的某个三元组i和前面的交换,但该三元组的bi<bx,且前面位置的Σaj更小,所以交换后仍不合法,所以这样不会更劣。对于ai<0就比较麻烦了,因为发现我们希望尽量按bi升序和按ai降序,但单独按其中一个排都能很容易的找到反例,所以我们按ai-bi降序排列。ai-bi的实际意义相当于后缀最大左括号-右括号数,添加过程中要求左括号数量始终不少于右括号。考虑反过来看,则要求右括号数量始终不少于左括号。由于ai<0,我们发现这个问题和之前的问题是相同的,只是左右括号反了过来,-(ai-bi)就相当于之前的bi。正确性就是这样了。
贪心顺序确定后,dp就很显然了,设f[i][j]为前i个括号序列左括号比右括号多j个时的答案即可。
果然检验出了我不会卡常数。为什么大家都跑的那么快啊。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; #define ll long long #define N 310 char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;} int gcd(int n,int m){return m==0?n:gcd(m,n%m);} int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int n,m,q,f[N][N*N]; char s[N]; struct data{int x,y,z; }a[N]; bool cmp(const data&a,const data&b) { return a.x>b.x; } bool cmp2(const data&a,const data&b) { return a.y>b.y; } bool cmp3(const data&a,const data&b) { return a.x-a.y>b.x-b.y; } int main() { #ifndef ONLINE_JUDGE freopen("bzoj4922.in","r",stdin); freopen("bzoj4922.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif n=read(); for (int i=1;i<=n;i++) { scanf("%s",s+1);a[i].z=strlen(s+1); for (int j=1;j<=a[i].z;j++) a[i].y=min(a[i].y,a[i].x+=(s[j]=='('?1:-1)),m+=s[j]=='('?1:0; } sort(a+1,a+n+1,cmp);int x=n+1; for (int i=1;i<=n;i++) if (a[i].x<0) {x=i;break;} sort(a+1,a+x,cmp2);sort(a+x,a+n+1,cmp3); memset(f,200,sizeof(f));f[0][0]=0; for (int i=1;i<=n;i++) for (int j=0;j<=m;j++) { f[i][j]=f[i-1][j]; if (j-a[i].x>=0&&j-a[i].x<=m&&j-a[i].x+a[i].y>=0) f[i][j]=max(f[i-1][j-a[i].x]+a[i].z,f[i][j]); } cout<<f[n][0]; return 0; }