codevs1906 最长递增子序列问题
2016-08-06 15:56
337 查看
题目描述 Description
给定正整数序列x1,..... , xn 。
(1)计算其最长递增子序列的长度s。
(2)计算从给定的序列中最多可取出多少个长度为s的递增子序列。
(3)如果允许在取出的序列中多次使用x1和xn,则从给定序列中最多可取出多少个长
度为s的递增子序列。
输入描述
Input Description
第1 行有1个正整数n,表示给定序列的长度。接
下来的1 行有n个正整数x1.....xn 。
输出描述
Output Description
第1 行是最长递增子序列的长度s。第2行是可取出的长度为s 的递增子序列个数。第3行是允许在取出的序列中多次使用x1和xn时可取出的长度为s 的递增子序列个数。
样例输入
Sample Input
4
3 6 2 5
样例输出
Sample Output
2
2
3
正解:建图+最大流
解题报告:
这道题的建图方式比较简单。第一问的话跑一下DP就可以了,nlogn的最长不下降子序列DP(n^2也可以),可以求出最大长度maxl。第二问考虑能从原序列中取出多少个长度为maxl的不下降序列。显然当我们选择了某个元素a[i]时,我们下一个可以选择的a[j],则a[j]是不小于当前元素且f[j]+1=f[i],我们考虑如何建图能达到题意要求。首先每个元素肯定只能用一次,并且只能按照上述顺序选取。
建图:每个点i我们拆成两个点i.a和i.b。对于每个f[i]=maxl的i我们从S向i.a连一条容量为1的边,f[i]=1的i我们从i.b向T连一条容量为1的边。并且每个点内部i.a向i.b连一条容量为1的边。然后对于每个i,都要从i.b向所有满足f[j]+1=f[i]且a[j]>=a[i]的j中的j.a都连一条容量为1的边。这样建完图之后,我们跑一遍最大流即可求出最多能取出多少个长度为maxl的不下降子序列。
考虑正确性。首先把每个点拆成两个,内部容量为1,则可以保证每个点最多被选一次。S向f[i]=maxl的点连边,可以保证我们取出的起点一定是有解而且正确的。f[i]=1同理。如此以来,我们就能得出第二问的答案。
第三问的话实际上就是在第二问的基础上加以改进。因为第一个元素和最后一个元素可以取多个,那么我们只需要去掉对于这两个元素的个数约束就可以了。所以S向1、n的边,1、n向T的边,内部的边,全部把容量改为无穷大就可以了。其余的和第二问没有区别。
给定正整数序列x1,..... , xn 。
(1)计算其最长递增子序列的长度s。
(2)计算从给定的序列中最多可取出多少个长度为s的递增子序列。
(3)如果允许在取出的序列中多次使用x1和xn,则从给定序列中最多可取出多少个长
度为s的递增子序列。
输入描述
Input Description
第1 行有1个正整数n,表示给定序列的长度。接
下来的1 行有n个正整数x1.....xn 。
输出描述
Output Description
第1 行是最长递增子序列的长度s。第2行是可取出的长度为s 的递增子序列个数。第3行是允许在取出的序列中多次使用x1和xn时可取出的长度为s 的递增子序列个数。
样例输入
Sample Input
4
3 6 2 5
样例输出
Sample Output
2
2
3
正解:建图+最大流
解题报告:
这道题的建图方式比较简单。第一问的话跑一下DP就可以了,nlogn的最长不下降子序列DP(n^2也可以),可以求出最大长度maxl。第二问考虑能从原序列中取出多少个长度为maxl的不下降序列。显然当我们选择了某个元素a[i]时,我们下一个可以选择的a[j],则a[j]是不小于当前元素且f[j]+1=f[i],我们考虑如何建图能达到题意要求。首先每个元素肯定只能用一次,并且只能按照上述顺序选取。
建图:每个点i我们拆成两个点i.a和i.b。对于每个f[i]=maxl的i我们从S向i.a连一条容量为1的边,f[i]=1的i我们从i.b向T连一条容量为1的边。并且每个点内部i.a向i.b连一条容量为1的边。然后对于每个i,都要从i.b向所有满足f[j]+1=f[i]且a[j]>=a[i]的j中的j.a都连一条容量为1的边。这样建完图之后,我们跑一遍最大流即可求出最多能取出多少个长度为maxl的不下降子序列。
考虑正确性。首先把每个点拆成两个,内部容量为1,则可以保证每个点最多被选一次。S向f[i]=maxl的点连边,可以保证我们取出的起点一定是有解而且正确的。f[i]=1同理。如此以来,我们就能得出第二问的答案。
第三问的话实际上就是在第二问的基础上加以改进。因为第一个元素和最后一个元素可以取多个,那么我们只需要去掉对于这两个元素的个数约束就可以了。所以S向1、n的边,1、n向T的边,内部的边,全部把容量改为无穷大就可以了。其余的和第二问没有区别。
//It is made by jump~ #include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> #include <cmath> #include <algorithm> #include <ctime> #include <vector> #include <queue> #include <map> #include <set> #ifdef WIN32 #define OT "%I64d" #else #define OT "%lld" #endif using namespace std; typedef long long LL; const int MAXN = 520; const int MAXM = 500011; const int inf = (1<<30); int n,a[MAXN],f[MAXN]; int b[MAXN],maxl; int S,T,ecnt,ans; int first[MAXN*2],deep[MAXN*2]; queue<int>Q; struct edge{ int to,next,f; }e[MAXM]; inline int getint() { int w=0,q=0; char c=getchar(); while((c<'0' || c>'9') && c!='-') c=getchar(); if (c=='-') q=1, c=getchar(); while (c>='0' && c<='9') w=w*10+c-'0', c=getchar(); return q ? -w : w; } inline void link(int x,int y,int z){ e[++ecnt].next=first[x]; first[x]=ecnt; e[ecnt].to=y; e[ecnt].f=z; e[++ecnt].next=first[y]; first[y]=ecnt; e[ecnt].to=x; e[ecnt].f=0; } inline int find(int x){ int l=1,r=maxl; int mid,ji=-1; while(l<=r) { mid=(l+r)/2; if(b[mid]>=x) l=mid+1,ji=mid; else r=mid-1; } if(ji==-1) return 0; return ji; } inline void build(){//建图,S向f[i]=maxl的点连边,每个点拆成两个,i拆为i和i+n,f[i]=1的点向T S=2*n+1; T=S+1; ecnt=1; for(int i=1;i<=n;i++) { link(i,i+n,1); if(f[i]==maxl) link(S,i,1); else if(f[i]==1) link(i+n,T,1); for(int j=i+1;j<=n;j++) { if(a[j]>=a[i] && f[j]+1==f[i]) link(i+n,j,1); } } } inline bool BFS(){ while(!Q.empty()) Q.pop(); memset(deep,0,sizeof(deep)); Q.push(S); deep[S]=1; while(!Q.empty()) { int u=Q.front(); Q.pop(); for(int i=first[u];i;i=e[i].next) { int v=e[i].to; if(e[i].f && !deep[v]) deep[v]=deep[u]+1,Q.push(v); } } if(!deep[T]) return false; return true; } inline int maxflow(int x,int remain){ if(x==T || remain==0) return remain; int flow=0,f; for(int i=first[x];i;i=e[i].next) { if(e[i].f && deep[e[i].to]==deep[x]+1) { int v=e[i].to; f=maxflow(v,min(remain,e[i].f)); if(f) { flow+=f; e[i].f-=f; e[i^1].f+=f; remain-=f; if(remain==0) return flow; }else deep[v]=-1; } } return flow; } inline void build_new(){ memset(first,0,sizeof(first)); S=2*n+1; T=S+1; ecnt=1; for(int i=1;i<=n;i++) { if(i==1 || i==n) { link(i,i+n,inf); if(f[i]==maxl) link(S,i,inf); else if(f[i]==1) link(i+n,T,inf); } else{ link(i,i+n,1); if(f[i]==maxl) link(S,i,1); else if(f[i]==1) link(i+n,T,1); } for(int j=i+1;j<=n;j++) { if(a[j]>=a[i] && f[j]+1==f[i]) link(i+n,j,1); } } } inline void work(){ n=getint(); for(int i=1;i<=n;i++) a[i]=getint(); for(int i=n;i>=1;i--) { f[i]=find(a[i])+1; if(b[f[i]]==0) b[f[i]]=a[i]; else b[f[i]]=max(a[i],b[f[i]]); maxl=max(maxl,f[i]); } printf("%d\n",maxl); build(); while(BFS()) ans+=maxflow(S,inf); if(ans==0) ans=n; printf("%d\n",ans); ans=0; build_new(); while(BFS()) ans+=maxflow(S,inf); if(ans==0) ans=n; printf("%d\n",ans); } int main() { work(); return 0; }
相关文章推荐
- 【codevs1906】[网络流24题]最长递增子序列问题
- codevs1906 最长递增子序列问题
- 【codevs1906】最长递增子序列问题 最大流
- [codevs1906]最长递增子序列(dp+dinic)
- [codevs 1906] 最长递增子序列问题
- [网络流24题][codevs1906]最长递增子序列(dp+isap)
- CODE(VS) 1044 导弹拦截 (判断最长递增和最长递减子序列)
- [codevs 1906] 最长递增子序列问题
- ★ 最长递增子序列问题 (最多不相交路径)(分层思想) 网络流最大流
- 网络流 6最长递增子序列问题
- 最长递增子序列问题
- 动态规划最长递增子序列问题
- 求最长递增子序列问题
- codevs1044 拦截导弹(最长不下降子序列dp)
- LIS&LCS最长递增子序列和最长公共子序列问题
- 最长递增子序列问题
- 最长递增子序列问题
- 最长递增子序列问题
- [转]最长递增子序列问题的求解
- 求数组中最长递增子序列问题