最长递增子序列问题 2011-12-29

算法实现题8-6 最长递增子序列问题(习题 8-17)
´问题描述:
给定正整数序列 n x x , ,
1  。
(1)计算其最长递增子序列的长度s。
(2)计算从给定的序列中最多可取出多少个长度为s的递增子序列。
(3)如果允许在取出的序列中多次使用x1和 xn,则从给定序列中最多可取出多少个长
度为s的递增子序列。
´编程任务:
设计有效算法完成(1)(2)(3)提出的计算任务。
´数据输入:
由文件input.txt提供输入数据。文件第1 行有 1个正整数n,表示给定序列的长度。接
下来的1 行有 n个正整数 n x x , ,
1  。
´结果输出:
程序运行结束时,将任务(1)(2)(3)的解答输出到文件 output.txt中。第1 行是最长
递增子序列的长度s。第2行是可取出的长度为s的递增子序列个数。第3行是允许在取出
的序列中多次使用x1和xn时可取出的长度为s的递增子序列个数。
输入文件示例  输出文件示例
input.txt 

4
3 6 2 5

output.txt 
2
2

————————————————

【问题分析】

第一问时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 Program Stone;
  2 var i,n,le,flow,s,t,ans:longint;
  3     a,f:array[0..10000]of longint;
  4     head,vh,dis,cur:array[0..20000]of longint;
  5     next,date,point:array[-50000..50000]of longint;
  6  Procedure add(x,y,z:longint);
  7   begin
  8     inc(le);
  9     date[le]:=z;
 10     point[le]:=y;
 11     next[le]:=head[x];
 12     head[x]:=le;
 13     point[-le]:=x;
 14     next[-le]:=head[y];
 15     head[y]:=-le;
 16   end;
 17  procedure main;
 18  var i,j,k,ins:longint;
 19   begin
 20      s:=0;t:=n+n+1;
 21     for i:=1 to n do
 22     begin
 23      f[i]:=1;
 24      for j:=1 to i-1 do
 25       if (a[j]<=a[i])and(f[j]+1>f[i]) then f[i]:=f[j]+1;
 26      if f[i]>ans then ans:=f[i];
 27     end;
 28     for i:=1 to n do
 29      begin
 30        if f[i]=1 then add(s,i,1);
 31        if f[i]=ans then add(i+n,t,1);
 32        add(i,i+n,1);
 33        for j:=1 to i-1 do
 34         if (f[j]+1=f[i])and(a[j]<=a[i]) then add(j+n,i,1);
 35      end;
 36   end;
 37  function min(a,b:longint):longint;
 38   begin
 39      if a<b then min:=a else min:=b;
 40   end;
 41 
 42  function aug(x,nf:longint):longint;
 43  var i,j,l,d,minh,ins:longint;
 44   begin
 45     if x=t then exit(nf);
 46     l:=nf;
 47     i:=cur[x];
 48     while i<>0 do
 49      begin
 50        if (date[i]>0)and(dis[point[i]]+1=dis[x]) then
 51           begin
 52              d:=aug(point[i],min(l,date[i]));
 53              cur[x]:=i;
 54              dec(date[i],d);
 55              inc(date[-i],d);
 56              dec(l,d);
 57              if (l=0)or(dis[0]=t+1) then exit(nf-l);
 58           end;
 59        i:=next[i];
 60      end;
 61    if l=nf then
 62      begin
 63         minh:=t+1;
 64         i:=head[x];
 65         while i<>0 do
 66          begin
 67            if (date[i]>0)and(dis[point[i]]<minh) then begin minh:=dis[point[i]];ins:=i;end;
 68            i:=next[i];
 69          end;
 70         cur[x]:=ins;
 71         dec(vh[dis[x]]);
 72         if vh[dis[x]]=0 then dis[0]:=t+1;
 73         dis[x]:=minh+1;
 74         inc(vh[dis[x]]);
 75      end;
 76     aug:=nf-l;
 77   end;
 78  procedure work;
 79  var i,j,k:longint;
 80   begin
 81     vh[0]:=t+1;
 82     for i:=s to t do cur[i]:=head[i];
 83     while dis[s]<t+1 do inc(flow,aug(s,maxint));
 84     writeln(flow);
 85     fillchar(dis,sizeof(dis),0);
 86     fillchar(vh,sizeof(vh),0);
 87     vh[0]:=t+1;
 88     add(1,1+n,maxint);
 89     add(n,n+n,maxint);
 90     add(s,1,maxint);
 91     add(n+n,t,maxint);
 92     for i:=s to t do cur[i]:=head[i];
 93     while dis[s]<t+1 do inc(flow,aug(s,maxint));
 94     writeln(flow);
 95   end;
 96 Begin
 97  assign(input,'prog86.in');assign(output,'prog86.out');
 98  reset(input);rewrite(output);
 99    readln(n);
100    for i:=1 to n do read(a[i]);
101    main;
102    writeln(ans);
103    work;
104  close(input);close(output);
105 end.
106 
107  

 

posted on 2016-03-02 20:35  Yesphet  阅读(245)  评论(0编辑  收藏  举报