最长递增子序列问题[网络流24题之6]

来源:互联网 发布:休闲鞋女淘宝 编辑:程序博客网 时间:2024/05/22 06:57

问题描述:

给定正整数序列 x1,x2,...,xn
1 计算其最长递增子序列的长度 s
2 计算从给定的序列中最多可取出多少个长度为 s 的递增子序列。
3 如果允许在取出的序列中多次使用 x1xn,则从给定序列中最多可取出多少个长度为 s 的递增子序列。


编程任务:

设计有效算法完成 123 提出的计算任务。


数据输入:

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


结果输出:

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


输入文件示例:

4
3 6 2 5


输出文件示例:

2
2
3


分析:

(1) 问是个经典的 DP 问题,因为数据范围不大,直接 n2的算法就可以了,不需要 nlogn的算法优化
(2) 问的求序列的个数,构建图 N 时就可以用到 n2 算法中求得的 cnt 数组 ( 逆序求 cnt 数组 ),每一次加边操作是,对于两个数 (ax,ay) 当且仅当 ax<=ay 并且 cntx=cnty+1 (这样就保证了每次加边的两个顶点的先后关系,不会出现求最大流时有路径的长度小于第 1 问的 ans )时将 (x,y) 加入图中,具体过程:

1 :新增源 S 和汇 T
2 :若 cntx=1 ,则在 Sx 中连一条容量为 1 的有向边
3 :若 cnty=ans ,则在 xT 之间连一条容量为 1 的有向边
4 :若 ax<=aycntx=cnty+1 ,则在 xy 之间连一条容量为 1 的有向边

(3) 问则只需要让边 (S,1)(n,T) 的容量改为 即可


代码:

#include <cstdio>#include <algorithm>#include <cstring>using namespace std;const int inf = 0x3f3f3f3f;int cnt[547],ans;int cur[547];int head[547],nxt[4747],to[4747],wei[4747],tot=1;int c[547];int que[547];int n;int a[547];int read();void add(int,int,int);bool bfs();bool dinic(int);int get_cnt();int main(){    n = read();    for(int i=1;i<=n;++i)        a[i] = read();    ans = get_cnt();    printf("%d\n",ans);    for(int i=n;i>=1;--i){        if(cnt[i] == 1)            add(i,546,1);        if(cnt[i] == ans)            add(545,i,1);        for(int j=i+1;j<=n;++j)            if(cnt[i]==cnt[j]+1 && a[j]>=a[i])                add(i,j,1);    }    int num=0;    while(bfs()){        memcpy(cur,head,sizeof head);        while(dinic(545))            ++num;    }    printf("%d\n",num);    for(int i=2;i<tot;i+=2){        wei[i] = 1;        wei[i^1] = 0;    }    for(int i=head[545];i;i=nxt[i])        if(to[i] == 1){            wei[i] = inf;            break;        }    for(int i=head[n];i;i=nxt[i])        if(to[i] == 546){            wei[i] = inf;            break;        }    num = 0;    while(bfs()){        memcpy(cur,head,sizeof head);        while(dinic(545))            num++;    }    printf("%d",num);    return 0;}int read(){    int in = 0;    char ch = getchar();    while(ch>'9'||ch<'0') ch = getchar();    while(ch<='9'&&ch>='0'){        in = in*10+ch-'0';        ch = getchar();    }    return in;}void add(int from,int tp,int value){    ++tot;nxt[tot]=head[from];head[from]=tot;to[tot]=tp;wei[tot]=value;    ++tot;nxt[tot]=head[tp];head[tp]=tot;to[tot]=from;wei[tot]=0;}int get_cnt(){    int m = 1;    for(int i=n;i>=1;--i){        cnt[i] = 1;        for(int j=i+1;j<=n;++j)            if(a[i]<=a[j] && cnt[j]>=cnt[i])                cnt[i] = cnt[j]+1;    }    for(int i=1;i<=n;++i)        if(cnt[i] > m)            m = cnt[i];    return m;}bool bfs(){    int now,H=0,T=1;    memset(c,0,sizeof c);    c[545] = 1;    que[1] = 545;    do{        now = que[++H];        for(int i=head[now];i;i=nxt[i])            if(!c[to[i]] && wei[i]){                c[to[i]] = c[now]+1;                que[++T] = to[i];            }    }while(H<T);    return c[546];}bool dinic(int place){    if(place == 546)        return true;    for(int i=head[place];i;i=nxt[i])        if(c[to[i]]==c[place]+1 && wei[i])            if(dinic(to[i])){                --wei[i];                ++wei[i^1];                return true;            }    return false;}
0 0