首页 > 代码库 > 网络流24题 最长递增子序列问题

网络流24题 最长递增子序列问题

题目描述

«问题描述:

给定正整数序列x1,...,xn 。

(1)计算其最长递增子序列的长度s。

(2)计算从给定的序列中最多可取出多少个长度为s的递增子序列。

(3)如果允许在取出的序列中多次使用x1和xn,则从给定序列中最多可取出多少个长度为s的递增子序列。

«编程任务:

设计有效算法完成(1)(2)(3)提出的计算任务。

输入输出格式

输入格式:

第1 行有1个正整数n,表示给定序列的长度。接下来的1 行有n个正整数n:x1, ..., xn。

输出格式:

第1 行是最长递增子序列的长度s。第2行是可取出的长度为s 的递增子序列个数。第3行是允许在取出的序列中多次使用x1和xn时可取出的长度为s 的递增子序列个数。 

输入输出样例

输入样例#1:
43 6 2 5
输出样例#1:
223

byvoid:

【问题分析】

第一问是LIS,动态规划求解,第二问和第三问用网络最大流解决。

【建模方法】

首先动态规划求出F[i],表示以第i位为开头的最长上升序列的长度,求出最长上升序列长度K。

1、把序列每位i拆成两个点<i.a>和<i.b>,从<i.a>到<i.b>连接一条容量为1的有向边。
2、建立附加源S和汇T,如果序列第i位有F[i]=K,从S到<i.a>连接一条容量为1的有向边。
3、如果F[i]=1,从<i.b>到T连接一条容量为1的有向边。
4、如果j>i且A[i] < A[j]且F[j]+1=F[i],从<i.b>到<j.a>连接一条容量为1的有向边。

求网络最大流,就是第二问的结果。把边(<1.a>,<1.b>)(<N.a>,<N.b>)(S,<1.a>)(<N.b>,T)这四条边的容量修改为无穷大,再求一次网络最大流,就是第三问结果。

【建模分析】

上述建模方法是应用了一种分层图的思想,把图每个顶点i按照F[i]的不同分为了若干层,这样图中从S出发到T的任何一条路径都是一个满足条件的最长上升子序列。由于序列中每个点要不可重复地取出,需要把每个点拆分成两个点。单位网络的最大流就是增广路的条数,所以最大流量就是第二问结果。第三问特殊地要求x1和xn可以重复使用,只需取消这两个点相关边的流量限制,求网络最大流即可。


 

其实比较好想的,能转移就连一条容量为1的边,选择次数不限制就INF
注意:
全部递减特判
#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>using namespace std;const int N=1005,M=1e6,INF=1e9;int read(){    char c=getchar();int x=0,f=1;    while(c<0||c>9){if(c==-)f=-1; c=getchar();}    while(c>=0&&c<=9){x=x*10+c-0; c=getchar();}    return x*f;}int n,a[N],f[N],g[N],s,t,l;void dp(){    for(int i=1;i<=n;i++) g[i]=INF;    for(int i=1;i<=n;i++){        int k=upper_bound(g+1,g+1+n,a[i])-g;        f[i]=k;        g[k]=a[i];        l=max(l,f[i]);    }}struct edge{    int v,ne,c,f;}e[M<<1];int cnt,h[N];inline void ins(int u,int v,int c){    cnt++;    e[cnt].v=v;e[cnt].c=c;e[cnt].f=0;e[cnt].ne=h[u];h[u]=cnt;    cnt++;    e[cnt].v=u;e[cnt].c=0;e[cnt].f=0;e[cnt].ne=h[v];h[v]=cnt;}void build(){    cnt=0;    memset(h,0,sizeof(h));    for(int i=1;i<=n;i++){        if(f[i]==1) ins(s,i,1);        ins(i,i+n,1);        if(f[i]==l) ins(i+n,t,1);        for(int j=1;j<i;j++) if(a[j]<=a[i]&&f[j]+1==f[i]) ins(j+n,i,1);    }}void build2(){    cnt=0;    memset(h,0,sizeof(h));    for(int i=1;i<=n;i++){        if(i==1||i==n){            if(f[i]==1) ins(s,i,INF);            ins(i,i+n,INF);            if(f[i]==l) ins(i+n,t,INF);        }else{            if(f[i]==1) ins(s,i,1);            ins(i,i+n,1);            if(f[i]==l) ins(i+n,t,1);        }        for(int j=1;j<i;j++) if(a[j]<=a[i]&&f[j]+1==f[i]) ins(j+n,i,1);    }}int vis[N],d[N],q[N],head=1,tail=1;bool bfs(){    memset(vis,0,sizeof(vis));    memset(d,0,sizeof(d));    head=tail=1;    q[tail++]=s;d[s]=0;vis[s]=1;    while(head!=tail){        int u=q[head++];        for(int i=h[u];i;i=e[i].ne){            int v=e[i].v;            if(!vis[v]&&e[i].c>e[i].f){                vis[v]=1;d[v]=d[u]+1;                q[tail++]=v;                if(v==t) return 1;            }        }    }    return 0;}int cur[N];int dfs(int u,int a){    if(u==t||a==0) return a;    int flow=0,f;    for(int &i=cur[u];i;i=e[i].ne){        int v=e[i].v;        if(d[v]==d[u]+1&&(f=dfs(v,min(a,e[i].c-e[i].f)))>0){            flow+=f;            e[i].f+=f;            e[((i-1)^1)+1].f-=f;            a-=f;            if(a==0) break;        }    }    return flow;}int dinic(){    int flow=0;    while(bfs()){//cout<<"p";        for(int i=s;i<=t;i++) cur[i]=h[i];        flow+=dfs(s,INF);    }    return flow;}int main(){    n=read();s=1;t=n+n+1;    for(int i=1;i<=n;i++) a[i]=read();    dp();printf("%d\n",l);    if(l==1) {printf("%d\n%d",n,n);return 0;}    build();    printf("%d\n",dinic());    build2();    printf("%d",dinic());}

 




网络流24题 最长递增子序列问题