最长单调递增子序列(网络24题,六)

来源:互联网 发布:软件国产化 东方通 编辑:程序博客网 时间:2024/05/01 07:46

                                                                     最长单调递增子序列


 由于别的OJ的判题系统的数据太水了。所以,这题我就给出我们OJ的这题链接吧。数据加强了很多!!!

 题目链接:Click Here~

 算法分析:

    是一道最多不相交路径的二分图,要你求出最多的路径。我们可以转换成求解最大流来做。第一次做最多不相交路径的题,卡了好久。好不容易在别的OJ过了,但是在自己OJ卡了半天,调试了好久终于过了^_^。好开心!!!

   对于题中的第一问,我们知道这是一个经典的算法,我就不再说了。不懂得自己查阅网上的资料吧,很多的。不过这里我们要用到动态规划的算法来求最长单调递增子序列,因为后面我们的建图将会用的到。由于题目要求每个数只能用一次,所以我们必须拆点。而网上百分之八十的代码都没有,也是因为大家相互抄袭的缘故,所以这题网上的代码基本都不是正解,因为HDU的数据水。所以,可以很轻松的AC。其实,正确的解法我也是学习By_void的网络流24题里知道的,但是他的建图好像错了。不过我的建图精华思想还是学他的。扯太多了,说一下这题的正解吧!

   用动态规划求出F[i],表示到第i位时候的最长单调递增序列长度。求出最长的递增序列长度Ans;

   1、把序列的每位拆成<i.a>和<i.b>把<i.a>和<i.b>之间连一条流为1的边。

   2、增加一个supersink and supersource,把F[i]=1的连接到supersource,F[i]=Ans的连接到supersink流为1.

   3、对A[i] < A[j] and F[i]+1 = F[j](i<j)的连接一条流为1的边。

【建模分析】

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


#include <iostream>#include <vector>#include <queue>#include <cstdio>#include <cstring>using namespace std;const int maxn = 1000+10;const int INF = ~0U >> 1;struct Edge{   int from,to,cap,flow;   Edge(){}   Edge(int _fr,int _t,int _c,int _f)       :from(_fr),to(_t),cap(_c),flow(_f){}};vector<Edge> edges;vector<int> G[maxn];int S,T,N,Ans,F[maxn],A[maxn];int d[maxn],cur[maxn];bool vst[maxn];void LIS(){    for(int i = 1;i <= N;++i){       F[i] = 1;       for(int j = 1;j < i;++j)          if(A[i] > A[j]&&F[i]<F[j]+1)             F[i] = F[j]+1;       if(F[i] > Ans)          Ans = F[i];   }}void Init(){    for(int i = 0;i < maxn;++i)        G[i].clear();    edges.clear();    Ans = 1;    S = 0,T = N+N+1;    for(int i = 1;i <= N;++i)        scanf("%d",&A[i]);}inline void AddEdge(int from,int to,int cap){    edges.push_back((Edge){from,to,cap,0});    edges.push_back((Edge){to,from,0,0,});    int sz = edges.size();    G[from].push_back(sz-2);    G[to].push_back(sz-1);}inline void Build(){    for(int i = 1;i <= N;++i){       AddEdge(i,i+N,1);       if(F[i]==1)          AddEdge(S,i,1);       else                //注意这里!!!       if(F[i]==Ans)          AddEdge(i+N,T,1);       for(int j = 1;j < i;++j)         if(A[i]>A[j]&&F[j]+1==F[i])            AddEdge(j+N,i,1);    }}bool BFS(){    memset(vst,false,sizeof(vst));    queue<int> Q;    Q.push(S);    d[S] = 0;    vst[S] = true;    while(!Q.empty()){        int u = Q.front();        Q.pop();        for(int i = 0;i < (int)G[u].size();++i){            Edge& e = edges[G[u][i]];            if(!vst[e.to]&&e.cap > e.flow){                vst[e.to] = true;                d[e.to] = d[u]+1;                Q.push(e.to);            }        }    }    return vst[T];}int DFS(int u,int a){    if(u==T||a==0)        return a;    int f,flow = 0;    for(int& i = cur[u];i < (int)G[u].size();++i){        Edge& e = edges[G[u][i]];        if(d[e.to]==d[u]+1&&(f=DFS(e.to,min(a,e.cap-e.flow)))>0){            e.flow += f;            edges[G[u][i]^1].flow -= f;            flow += f;            a -= f;            if(a==0)break;        }    }    return flow;}int Maxflow(){    int flow = 0;    while(BFS()){        memset(cur,0,sizeof(cur));        flow += DFS(S,INF);    }    return flow;}void Solve(){    LIS();    printf("%d\n",Ans);    if(Ans==1){            //注意这里!!跟前面的else有关(当Ans=1时)        printf("%d\n",N);        return;    }    Build();    int flow = Maxflow();    printf("%d\n",flow);}int main(){    while(~scanf("%d",&N)){        Init();        Solve();    }    return 0;}


加强的数据:

 Input

4

1    1     2     2


5

5    4     3     2    1


4

3    6      2     5


5

1     1      2      3      3


Output(长度为Ans的个数)

2

5

2

1


                                  最长递增子序列问题

    问题描述: 给定正整数序列x1,...,xn 。

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

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

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

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

   ́数据输入: 由文件 input.txt 提供输入数据。文件第 1 行有 1 个正整数 n,表示给定序列的长度。接 下来的 1 行有 n 个正整数 x1 ,.., xn 。 

   结果输出: 程序运行结束时,将任务(1)(2)(3)的解答输出到文件 output.txt 中。

   第 1 行是最长 递增子序列的长度 s。

   第 2 行是可取出的长度为 s 的递增子序列个数。

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

   输入文件示例 input.txt 4 3 6 2 5

   输出文件示例 output.txt 2 2 3






8 0
原创粉丝点击