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

Posted Candy?

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络流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:
4
3 6 2 5
输出样例#1:
2
2
3

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题 最长递增子序列问题的主要内容,如果未能解决你的问题,请参考以下文章

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

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

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

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

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

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