最长递增子序列问题
2017-10-30 20:26
183 查看
题目描述
«问题描述:给定正整数序列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 的递增子序列个数。
输入输出样例
输入样例#1:复制
4 3 6 2 5
输出样例#1: 复制
2 2 3
说明
n≤500n\le 500n≤500摘自洛谷题解https://www.luogu.org/wiki/show?name=%E9%A2%98%E8%A7%A3+P2766
写的很清楚
[b]首先动态规划求出F[i],表示以第i位为开头的最长上升序列的长度,求出最长上升序列长度K。[/b]
[b]1、把序列每位i拆成两个点<i.a>和<i.b>,从<i.a>到<i.b>连接一条容量为1的有向边。[/b]
[b]2、建立附加源S和汇T,如果序列第i位有F[i]=K,从S到<i.a>连接一条容量为1的有向边。[/b]
[b]3、如果F[i]=1,从<i.b>到T连接一条容量为1的有向边。[/b]
[b]4、如果j>i且A[i] < A[j]且F[j]+1=F[i],从<i.b>到<j.a>连接一条容量为1的有向边。[/b]
[b]求网络最大流,就是第二问的结果。把边(<1.a>,<1.b>)(<N.a>,<N.b>) (S,<1.a>)(<N.b>,T)这四条边的容量修改为无穷大,再求一次网络最大流,就是第三问结果。[/b]
[b]【建模分析】[/b]
[b]上述建模方法是应用了一种分层图的思想,把图每个顶点i按照F[i]的不同分为了若干层,这样图中从S出发到T的任何一条路径都是一个满足条件的最长上升子序列。[/b]
[b]由于序列中每个点要不可重复地取出,需要把每个点拆分成两个点。单位网络的最大流就是增广路的条数,所以最大流量就是第二问结果。[/b]
[b]第三问特殊地要求x1和xn可以重复使用,只需取消这两个点相关边的流量限制,求网络最大流即可。[/b]
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<queue> using namespace std; struct Node { int next,to,c; }edge[2000001],edge2[2000001]; int num=1,head[2001],cur[2001],n,m,dist[2001],ans,sum,maxflow,a[2001]; int f[2001],s,inf=2e9; void add(int u,int v,int c) { num++; edge[num].next=head[u]; head[u]=num; edge[num].to=v; edge[num].c=c; } bool bfs(int S,int T) {int i; memset(dist,-1,sizeof(dist)); queue<int>Q; Q.push(S); dist[S]=1; while (!Q.empty()) { int u=Q.front(); Q.pop(); for (i=head[u];i;i=edge[i].next) { int v=edge[i].to; if (edge[i].c>0&&dist[v]==-1) { dist[v]=dist[u]+1; Q.push(v); } } } if (dist[T]==-1) return 0; return 1; } int dfs(int x,int flow,int des) { int res=0; if (x==des) return flow; for (int &i=cur[x];i;i=edge[i].next) { int v=edge[i].to; if (dist[v]==dist[x]+1&&edge[i].c) { int tmp=dfs(v,min(flow-res,edge[i].c),des); if (tmp<0) continue; edge[i].c-=tmp; edge[i^1].c+=tmp; res+=tmp; if (res==flow) return res; } } return res; } void Dinic(int S,int T) { maxflow=0; while (bfs(S,T)) { memcpy(cur,head,sizeof(cur)); int a=0; while (a=dfs(S,2e9,T)) maxflow+=a; } return; } int main() {int i,j; cin>>n; for (i=1;i<=n;i++) { scanf("%d",&a[i]); } for (i=1;i<=n;i++) { for (j=0;j<=i-1;j++) if (a[i]>=a[j]) f[i]=max(f[i],f[j]+1); s=max(s,f[i]); } for (i=1;i<=n;i++) if (f[i]==1) add(0,i,1),add(i,0,0); for (i=1;i<=n;i++) if (f[i]==s) add(n+i,2*n+1,1),add(2*n+1,n+i,0); for (i=1;i<=n;i++) add(i,n+i,1),add(n+i,i,0); for (i=1;i<=n;i++) for (j=1;j<=i-1;j++) if (a[i]>=a[j]&&f[i]==f[j]+1) add(n+j,i,1),add(i,n+j,0); memcpy(edge2,edge,sizeof(edge)); Dinic(0,2*n+1); cout<<s<<endl; cout<<maxflow<<endl; memcpy(edge,edge2,sizeof(edge)); for (i=head[1];i;i=edge[i].next) { int v=edge[i].to; if (v==n+1) { edge[i].c=inf; } if (v==0) { edge[i^1].c=inf; } } for (i=head[2*n];i;i=edge[i].next) { int v=edge[i].to; if (v==n) { edge[i^1].c=inf; } if (v==2*n+1) { edge[i].c=inf; } } Dinic(0,2*n+1); cout<<maxflow; }
相关文章推荐
- 最长递增子序列问题
- 最长递增子序列问题
- 最长递增子序列问题
- 华为OJ训练题——最长递增子序列问题
- 最长递增子序列问题 nyoj 17单调递增最长子序列 nyoj 79拦截导弹
- 最长递增子序列问题
- 最长递增子序列问题
- 最长递增子序列问题
- 最长公共子串(子序列)、最长递增子序列、最长回文子串等问题
- 最长递增子序列问题
- 最长递增子序列长度问题
- 最长递增子序列问题—LIS
- 求一个数组的最长递增子序列(动态规划经典问题)
- 用O(nlog(n)实现最长递增子序列问题
- 最长递增子序列问题的求解(LIS)
- 最长递增子序列问题的求解(LIS)
- 最长递增子序列问题的求解
- 最长递增子序列问题的求解
- 关于最长递增子序列问题的求解(LIS)
- 最长递增子序列问题的求解