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

731. [网络流24题] 最长递增子序列

★★★☆   输入文件:alis.in   输出文件:alis.out   简单对比
时间限制:1 s   内存限制:128 MB


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

注意:这里的最长递增子序列即最长不下降子序列!!!
«编程任务:
设计有效算法完成(1)(2)(3)提出的计算任务。
«数据输入:
由文件alis.in提供输入数据。文件第1 行有1个正整数n(n<=500),表示给定序列的长度。接
下来的1 行有n个正整数x1,..., xn。
«结果输出:
程序运行结束时,将任务(1)(2)(3)的解答输出到文件alis.out中。第1 行是最长
递增子序列的长度s。第2行是可取出的长度为s 的递增子序列个数。第3行是允许在取出
的序列中多次使用x1和xn时可取出的长度为s 的递增子序列个数。
输入文件示例 输出文件示例
alis.in
4

3 6 2 5

alis.out

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可以重复使用,只需取消这两个点相关边的流量限制,求网络最大流即可。

转载自hzwer

ps:任务3若能取出无限多的序列,则输出任务2的答案 

  1. //数据略坑
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. #define setfile(name) freopen(#name".in","r",stdin);freopen(#name".out","w",stdout);
  6. using namespace std;
  7. const int N=1005;
  8. const int inf=1e9;
  9. struct edge{int v,cap,next;}e[N*N*2];int tot=1,head[N];
  10. int n,S,T,K,ans,a[N],f[N],dis[N],q[N*N*2];
  11. inline int read(){
  12. int x=0,f=1;char ch=getchar();
  13. while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
  14. while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
  15. return x*f;
  16. }
  17. inline void add(int x,int y,int z){
  18. e[++tot].v=y;e[tot].cap=z;e[tot].next=head[x];head[x]=tot;
  19. e[++tot].v=x;e[tot].cap=0;e[tot].next=head[y];head[y]=tot;
  20. }
  21. inline bool bfs(){
  22. memset(dis,-1,sizeof dis);
  23. unsigned short h=0,t=1;q[t]=S;dis[S]=0;
  24. while(h!=t){
  25. int x=q[++h];
  26. for(int i=head[x];i;i=e[i].next){
  27. if(e[i].cap&&dis[e[i].v]==-1){
  28. dis[e[i].v]=dis[x]+1;
  29. if(e[i].v==T) return 1;
  30. q[++t]=e[i].v;
  31. }
  32. }
  33. }
  34. return 0;
  35. }
  36. int dfs(int x,int f){
  37. if(x==T) return f;
  38. int used=0,t;
  39. for(int i=head[x];i;i=e[i].next){
  40. if(e[i].cap&&dis[e[i].v]==dis[x]+1){
  41. t=dfs(e[i].v,min(e[i].cap,f));
  42. e[i].cap-=t;e[i^1].cap+=t;
  43. used+=t;f-=t;
  44. if(!f) return used;
  45. }
  46. }
  47. if(!used) dis[x]=-1;
  48. return used;
  49. }
  50. inline int dinic(){
  51. int res=0;
  52. while(bfs()) res+=dfs(S,inf);
  53. return res;
  54. }
  55. inline void DP(){
  56. for(int i=1;i<=n;i++){
  57. f[i]=1;
  58. for(int j=1;j<i;j++){
  59. if(a[i]>=a[j]){
  60. f[i]=max(f[i],f[j]+1);
  61. }
  62. }
  63. }
  64. K=*max_element(f+1,f+n+1);
  65. }
  66. void init(){
  67. n=read();
  68. for(int i=1;i<=n;i++) a[i]=read();
  69. }
  70. void ord_mapping(){
  71. tot=1;memset(head,0,sizeof head);
  72. for(int i=1;i<=n;i++){
  73. for(int j=1;j<i;j++){
  74. if(a[j]<=a[i]&&f[j]+1==f[i]){
  75. add(j+n,i,1);
  76. }
  77. }
  78. }
  79. }
  80. inline void work(){
  81. DP();printf("%d ",K);
  82. S=0,T=n<<1|1;
  83. ord_mapping();
  84. for(int i=1;i<=n;i++){
  85. if(f[i]==1) add(S,i,1);
  86. if(f[i]==K) add(i+n,T,1);
  87. add(i,i+n,1);
  88. }
  89. ans=dinic();
  90. printf("%d ",ans);
  91. ord_mapping();
  92. for(int i=1;i<=n;i++){
  93. int w=1;
  94. if(i==1||i==n) w=inf;
  95. if(f[i]==1) add(S,i,w);
  96. if(f[i]==K) add(i+n,T,w);
  97. add(i,i+n,w);
  98. }
  99. int lastans=ans;
  100. ans=dinic();
  101. if(ans>inf) printf("%d ",lastans);
  102. else printf("%d ",ans);
  103. }
  104. int main(){
  105. setfile(alis);
  106. init();
  107. work();
  108. return 0;
  109. }


原文地址:https://www.cnblogs.com/shenben/p/6538561.html