最长递增子序列问题 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
3
————————————————
【问题分析】
第一问时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