传送门
题解:一个’(”)’串合法当且仅当,任意位置左括号个数前缀和-右括号个数前缀和不小于0且末尾的左括号个数前缀和=右括号个数前缀和。用这个sum正向逆向各做一次01背包,最后存在一对dp值不小于0,则可以将两个子串匹配为一个整串
主要是新学一种01背包的用途,代码实现细节太狗血了。。。打了’?’都是不太清楚但是又不得不这样写的地方orz
P.S.这种写法比网上另一种写法快7倍,内存也省了非常多,简直玄学。。。
#include<bits/stdc++.h>
using namespace std;
const int MAXN=305,INF=0x3f3f3f3f;
int n;
char ss[MAXN][MAXN];
int sum[MAXN],mn1[MAXN],mn2[MAXN];
struct NODE {
int x,num,len;
friend bool operator <(const NODE &p,const NODE &q) {
return p.x>q.x;
}
}a[MAXN],b[MAXN];
int t1=0,t2=0;
int dp1[MAXN*MAXN],dp2[MAXN*MAXN];
int main() {
// freopen("bzoj 4922.in","r",stdin);
scanf("%d",&n);
memset(dp1,128,sizeof(dp1));//???
memset(dp2,128,sizeof(dp2));
for (int i=1;i<=n;++i) {
scanf("%s",ss[i]+1);
int L=strlen(ss[i]+1),k=0;
for (int j=1;j<=L;++j) {
sum[i]+=ss[i][j]=='('?1:-1;
mn1[i]=min(mn1[i],sum[i]);
}
for (int j=L;j;--j) {
k+=ss[i][j]==')'?1:-1;
mn2[i]=min(mn2[i],k);
}
sum[i]>=0?a[++t1]=(NODE){mn1[i],sum[i],L}:b[++t2]=(NODE){mn2[i],-sum[i],L};
}
sort(a+1,a+t1+1);
sort(b+1,b+t2+1);
dp1[0]=dp2[0]=0;
for (int i=1;i<=t1;++i)
for (int j=i*MAXN;j>=a[i].num-a[i].x;--j)
//一些串中间某位置sum小于0为非法转移,所以j>=a[i].num-a[i].x
if (j-a[i].num>=0) dp1[j]=max(dp1[j],dp1[j-a[i].num]+a[i].len);
for (int i=1;i<=t2;++i)
for (int j=i*MAXN;j>=b[i].num-b[i].x;--j)
if (j-b[i].num>=0) dp2[j]=max(dp2[j],dp2[j-b[i].num]+b[i].len);
int ans=0;
for (int i=MAXN*MAXN;~i;--i)//MAXN--AC?n--WA?
if (dp1[i]>=0&&dp2[i]>=0) ans=max(ans,dp1[i]+dp2[i]);
printf("%d\n",ans);
return 0;
}