首页 > 代码库 > 网络流24题 最长递增子序列问题
网络流24题 最长递增子序列问题
题目描述
«问题描述:
给定正整数序列x1,...,xn 。
(1)计算其最长递增子序列的长度s。
(2)计算从给定的序列中最多可取出多少个长度为s的递增子序列。
(3)如果允许在取出的序列中多次使用x1和xn,则从给定序列中最多可取出多少个长度为s的递增子序列。
«编程任务:
设计有效算法完成(1)(2)(3)提出的计算任务。
输入输出格式
输入格式:
第1 行有1个正整数n,表示给定序列的长度。接下来的1 行有n个正整数n:x1, ..., xn。
输出格式:
第1 行是最长递增子序列的长度s。第2行是可取出的长度为s 的递增子序列个数。第3行是允许在取出的序列中多次使用x1和xn时可取出的长度为s 的递增子序列个数。
输入输出样例
43 6 2 5
223
byvoid:
【问题分析】
第一问是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的边,选择次数不限制就INF
注意:
全部递减特判
#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>using namespace std;const int N=1005,M=1e6,INF=1e9;int read(){ char c=getchar();int x=0,f=1; while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1; c=getchar();} while(c>=‘0‘&&c<=‘9‘){x=x*10+c-‘0‘; c=getchar();} return x*f;}int n,a[N],f[N],g[N],s,t,l;void dp(){ for(int i=1;i<=n;i++) g[i]=INF; for(int i=1;i<=n;i++){ int k=upper_bound(g+1,g+1+n,a[i])-g; f[i]=k; g[k]=a[i]; l=max(l,f[i]); }}struct edge{ int v,ne,c,f;}e[M<<1];int cnt,h[N];inline void ins(int u,int v,int c){ cnt++; e[cnt].v=v;e[cnt].c=c;e[cnt].f=0;e[cnt].ne=h[u];h[u]=cnt; cnt++; e[cnt].v=u;e[cnt].c=0;e[cnt].f=0;e[cnt].ne=h[v];h[v]=cnt;}void build(){ cnt=0; memset(h,0,sizeof(h)); for(int i=1;i<=n;i++){ if(f[i]==1) ins(s,i,1); ins(i,i+n,1); if(f[i]==l) ins(i+n,t,1); for(int j=1;j<i;j++) if(a[j]<=a[i]&&f[j]+1==f[i]) ins(j+n,i,1); }}void build2(){ cnt=0; memset(h,0,sizeof(h)); for(int i=1;i<=n;i++){ if(i==1||i==n){ if(f[i]==1) ins(s,i,INF); ins(i,i+n,INF); if(f[i]==l) ins(i+n,t,INF); }else{ if(f[i]==1) ins(s,i,1); ins(i,i+n,1); if(f[i]==l) ins(i+n,t,1); } for(int j=1;j<i;j++) if(a[j]<=a[i]&&f[j]+1==f[i]) ins(j+n,i,1); }}int vis[N],d[N],q[N],head=1,tail=1;bool bfs(){ memset(vis,0,sizeof(vis)); memset(d,0,sizeof(d)); head=tail=1; q[tail++]=s;d[s]=0;vis[s]=1; while(head!=tail){ int u=q[head++]; for(int i=h[u];i;i=e[i].ne){ int v=e[i].v; if(!vis[v]&&e[i].c>e[i].f){ vis[v]=1;d[v]=d[u]+1; q[tail++]=v; if(v==t) return 1; } } } return 0;}int cur[N];int dfs(int u,int a){ if(u==t||a==0) return a; int flow=0,f; for(int &i=cur[u];i;i=e[i].ne){ int v=e[i].v; if(d[v]==d[u]+1&&(f=dfs(v,min(a,e[i].c-e[i].f)))>0){ flow+=f; e[i].f+=f; e[((i-1)^1)+1].f-=f; a-=f; if(a==0) break; } } return flow;}int dinic(){ int flow=0; while(bfs()){//cout<<"p"; for(int i=s;i<=t;i++) cur[i]=h[i]; flow+=dfs(s,INF); } return flow;}int main(){ n=read();s=1;t=n+n+1; for(int i=1;i<=n;i++) a[i]=read(); dp();printf("%d\n",l); if(l==1) {printf("%d\n%d",n,n);return 0;} build(); printf("%d\n",dinic()); build2(); printf("%d",dinic());}
网络流24题 最长递增子序列问题