【网络流24题】最长递增子序列(DP+最大流)

来源:互联网 发布:一键抠图软件怎么清理 编辑:程序博客网 时间:2024/05/22 03:31

传送门

    最长递增子序列
    题意:计算最长不降子序列长度s与数量,以及多次使用首末元素之后最多取出多少长度为s的最长不降子序列。

I think

    第一问DP求出f[i],表示以第i位为首位的最长不降子序列长度len,二三问网络流求解。增设源汇点S T,S向f[i]==len的点i连边,f[i]==1的点向T连边,满足i< j&&a[i]< =a[j]&&f[i]==f[j]+1的点对 < i,j > 连边,边的容量均为1,,跑最大流即为答案。第三问将S向1,N向T的连边容量更改为Inf,再次跑网络流得解。

Code

#include<cstdio>#include<cstring>#include<queue>using namespace std;const int sm = 500+5;const int sn = 125750;const int Inf = 0x3f3f3f3f;int x,y,Flw;int N,M,S,T,tot=1,len;int a[sm],f[sm],lev[sm],cur[sm];int to[sn],hd[sm],c[sn],nxt[sn],_c[sn];int Max(int x,int y) { return x>y?x:y; }int Min(int x,int y) { return x<y?x:y; }void Add(int u,int v,int w) {    to[++tot]=v,nxt[tot]=hd[u],hd[u]=tot,c[tot]=_c[tot]=w;    to[++tot]=u,nxt[tot]=hd[v],hd[v]=tot,c[tot]=_c[tot]=0;}bool Bfs() {    for(int i=1;i<=T;++i) lev[i]=0;    int t; queue<int>q;    q.push(S),lev[S]=1;    while(!q.empty()) {        t=q.front(),q.pop();        for(int i=hd[t];i;i=nxt[i])             if(c[i]>0&&!lev[to[i]]) {                lev[to[i]]=lev[t]+1;                if(to[i]==T) return 1;                q.push(to[i]);            }    }    return 0;}int Dfs(int x,int mx) {    if(x==T||!mx) return mx;    int f;    for(int i=cur[x]?cur[x]:hd[x];i;i=nxt[i]) {        cur[x]=i;        if(c[i]&&lev[to[i]]==lev[x]+1)            if(f=Dfs(to[i],Min(mx,c[i])))                return c[i]-=f,c[i^1]+=f,f;    }    return 0;} void Dinic() {    int f; Flw=0;    while(Bfs()) {        for(int i=1;i<=T;++i) cur[i]=0;        while(f=Dfs(S,Inf)) Flw+=f;    }}int main() {    scanf("%d",&N);    for(int i=1;i<=N;++i)        scanf("%d",&a[i]);    for(int i=N;i>=1;--i) {        f[i]=1;        for(int j=i+1;j<=N;++j)            if(a[j]>=a[i])                 f[i]=Max(f[i],f[j]+1);        len=Max(len,f[i]);    }    S=N+1,T=S+1;    for(int i=1;i<=N;++i) {        if(f[i]==len) {            if(i==1) x=tot+1;            Add(S,i,1);        }        if(f[i]==1) {            if(i==N) y=tot+1;            Add(i,T,1);        }    }    for(int i=1;i<N;++i)        for(int j=i+1;j<=N;++j)            if(a[j]>=a[i]&&f[j]+1==f[i])                    Add(i,j,1);    Dinic();    printf("%d\n%d\n",len,Flw);    if(!x&&!y) printf("%d\n",Flw);    else {        memcpy(c,_c,sizeof(c));        if(x) c[x]=Inf;        if(y) c[y]=Inf;        Dinic();        printf("%d\n",Flw);    }    return 0;}
阅读全文
0 0