bzoj 4922(背包dp)

来源:互联网 发布:淘宝网代卖怎么做的 编辑:程序博客网 时间:2024/06/15 22:01

传送门
题解:一个’(”)’串合法当且仅当,任意位置左括号个数前缀和-右括号个数前缀和不小于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;}
原创粉丝点击