最长递增子序列问题
2017-03-16 20:46
417 查看
问题描述
给定正整数序列x1,···,xn。 (1)计算其最长递增子序列的长度s。 (2)计算从给定的序列中最多可取出多少个长度为s的递增子序列。 (3)如果允许在取出的序列中多次使用x1和 xn,则从给定序列中最多可取出多少个长度为s的递增子序列。
编程任务
设计有效算法完成(1)(2)(3)提出的计算任务。
数据输入
由文件input.txt提供输入数据。文件第1行有1个正整数n,表示给定序列的长度。接下来的1行有n个正整数x1,···,xn。
结果输出
程序运行结束时,将任务(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
题解
问题(1)可以用dp进行求解。对于问题(2)和(3),我们可以用网络流解决。对于每一个数字,我们都看作节点并进行拆点,以达到限流的目的。对于每一个dp值为1,也就是以这个数为结尾的最长上升子序列长度为1,的节点,我们可以从源点引一条边连向这个点。同理,对于每一个dp值最大的节点,我们从它引一条边连向汇点。这种建图方式也蕴含了分层图的思想。(即从源点开始,bfs到的点分别是dp为1、2、3的点)
因为我们要构建分层图,所以对于每一个f[i]==f[j]+1,s[i]>=s[j] (j < i)的情况,我们从j拆点后的点引一条边连向i点(本人一开始连边的时候是从i的拆点指向j,但是去掉f[i]==f[j]+1的条件可以得到82分,我能说是数据太水了么)
CODE:
#include<cstdio> #include<cstring> const int INF=1e9; struct queue { int h,t; int a[10001]; inline void clear(){h=1,t=0;} inline void push(int n){a[++t]=n;} inline int front(){return a[h];} inline void pop(){h++;} inline bool empty(){return h>t;} }q; struct edge { int next,to,remain; }a[200001]; int head[1001]; int deep[1001]; int s[1001]; int f[1001]; int n,num=1,ans,S,T; inline int max(int a,int b){return a>b?a:b;} inline int min(int a,int b){return a<b?a:b;} inline void add(int x,int y,int cap) { a[++num].next=head[x],a[num].to=y,a[num].remain=cap,head[x]=num; a[++num].next=head[y],a[num].to=x,head[y]=num; } inline bool bfs() { memset(deep,0x3f,sizeof(deep)); deep[S]=0; q.clear();q.push(S); while(!q.empty()) { int tmp=q.front();q.pop(); for(int i=head[tmp];i;i=a[i].next) if(deep[a[i].to]>INF&&a[i].remain) q.push(a[i].to),deep[a[i].to]=deep[tmp]+1; } return deep[T]<INF; } int dfs(int now,int limit) { if(now==T||!limit) return limit; int flow=0,f; for(int i=head[now];i;i=a[i].next) if(deep[a[i].to]==deep[now]+1&&(f=dfs(a[i].to,min(limit,a[i].remain)))) { limit-=f,flow+=f,a[i].remain-=f,a[i^1].remain+=f; if(!limit) return flow; } deep[now]=-1; return flow; } inline int dinic() { int ans=0; while(bfs()) ans+=dfs(S,INF); return ans; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&s[i]); f[1]=1; for(int i=2;i<=n;i++) { f[i]=1; for(int j=1;j<i;j++) if(s[j]<=s[i]) f[i]=max(f[i],f[j]+1); ans=max(ans,f[i]); } printf("%d\n",ans); //1~n:原点 n+1~2*n:拆点 2*n+1:源点 2*n+2:汇点 S=n<<1|1;T=S+1; for(int i=1;i<=n;i++) add(i,n+i,1); for(int i=1;i<=n;i++) { if(f[i]==1) add(S,i,1); if(f[i]==ans) add(n+i,T,1); } for(int i=2;i<=n;i++) for(int j=1;j<i;j++) if(s[j]<=s[i]&&f[j]==f[i]-1) add(n+j,i,1); ans=dinic();printf("%d\n",ans); for(int i=head[S];i;i=a[i].next) if(a[i].to==1) {a[i].remain=INF;break;} for(int i=head[1];i;i=a[i].next) if(a[i].to==n+1) {a[i].remain=INF;break;} for(int i=head[n<<1];i;i=a[i].next) if(a[i].to==T) {a[i].remain=INF;break;} for(int i=head ;i;i=a[i].next) if(a[i].to==n<<1) {a[i].remain=INF;break;} ans+=dinic();printf("%d",ans); return 0; }
相关文章推荐
- 关于最长递增子序列问题的求解(LIS)
- 最长递增子序列长度问题
- 最长递增子序列问题—LIS
- 最长递增子序列问题的求解
- 最长递增子序列问题
- 最长递增子序列问题的求解
- 最长递增子序列问题
- 最长递增子序列问题
- 最长递增子序列问题
- 最长递增子序列(LIS)问题
- 求一个数组的最长递增子序列(动态规划经典问题)
- 用O(nlog(n)实现最长递增子序列问题
- 最长递增子序列问题的求解
- 最长递增子序列(LIS)问题
- 最长递增子序列问题的求解
- 关于最长递增子序列问题的求解(LIS)
- 最长递增子序列问题的求解
- 最长递增子序列问题 nyoj 17单调递增最长子序列 nyoj 79拦截导弹
- 华为OJ训练题——最长递增子序列问题
- 最长递增子序列问题