codevs1906 最长递增子序列问题

题目描述 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的最长不下降子序列DPn^2也可以),可以求出最大长度maxl。第二问考虑能从原序列中取出多少个长度为maxl的不下降序列。显然当我们选择了某个元素a[i]时,我们下一个可以选择的a[j],则a[j]是不小于当前元素且f[j]+1=f[i],我们考虑如何建图能达到题意要求。首先每个元素肯定只能用一次,并且只能按照上述顺序选取。

  建图:每个点i我们拆成两个点i.ai.b。对于每个f[i]=maxli我们从Si.a连一条容量为1的边,f[i]=1i我们从i.bT连一条容量为1的边。并且每个点内部i.ai.b连一条容量为1的边。然后对于每个i,都要从i.b向所有满足f[j]+1=f[i]且a[j]>=a[i]j中的j.a都连一条容量为1的边。这样建完图之后,我们跑一遍最大流即可求出最多能取出多少个长度为maxl的不下降子序列。

  考虑正确性。首先把每个点拆成两个,内部容量为1,则可以保证每个点最多被选一次。Sf[i]=maxl的点连边,可以保证我们取出的起点一定是有解而且正确的。f[i]=1同理。如此以来,我们就能得出第二问的答案。

  第三问的话实际上就是在第二问的基础上加以改进。因为第一个元素和最后一个元素可以取多个,那么我们只需要去掉对于这两个元素的个数约束就可以了。所以S1n的边,1nT的边,内部的边,全部把容量改为无穷大就可以了。其余的和第二问没有区别。

 
  1 //It is made by jump~
  2 #include <iostream>
  3 #include <cstdlib>
  4 #include <cstring>
  5 #include <cstdio>
  6 #include <cmath>
  7 #include <algorithm>
  8 #include <ctime>
  9 #include <vector>
 10 #include <queue>
 11 #include <map>
 12 #include <set>
 13 #ifdef WIN32   
 14 #define OT "%I64d"
 15 #else
 16 #define OT "%lld"
 17 #endif
 18 using namespace std;
 19 typedef long long LL;
 20 const int MAXN = 520;
 21 const int MAXM = 500011;
 22 const int inf = (1<<30);
 23 int n,a[MAXN],f[MAXN];
 24 int b[MAXN],maxl;
 25 int S,T,ecnt,ans;
 26 int first[MAXN*2],deep[MAXN*2];
 27 queue<int>Q;
 28 struct edge{
 29     int to,next,f;
 30 }e[MAXM];
 31 
 32 inline int getint()
 33 {
 34        int w=0,q=0;
 35        char c=getchar();
 36        while((c<'0' || c>'9') && c!='-') c=getchar();
 37        if (c=='-')  q=1, c=getchar();
 38        while (c>='0' && c<='9') w=w*10+c-'0', c=getchar();
 39        return q ? -w : w;
 40 }
 41 
 42 inline void link(int x,int y,int z){
 43     e[++ecnt].next=first[x]; first[x]=ecnt; e[ecnt].to=y; e[ecnt].f=z;
 44     e[++ecnt].next=first[y]; first[y]=ecnt; e[ecnt].to=x; e[ecnt].f=0;
 45 }
 46 
 47 inline int find(int x){
 48     int l=1,r=maxl; int mid,ji=-1;
 49     while(l<=r) {
 50     mid=(l+r)/2;
 51     if(b[mid]>=x) l=mid+1,ji=mid;
 52     else r=mid-1;
 53     }
 54     if(ji==-1) return 0;
 55     return ji;
 56 }
 57 
 58 inline void build(){//建图,S向f[i]=maxl的点连边,每个点拆成两个,i拆为i和i+n,f[i]=1的点向T
 59     S=2*n+1; T=S+1;  ecnt=1;
 60     for(int i=1;i<=n;i++) {
 61     link(i,i+n,1);
 62     if(f[i]==maxl) link(S,i,1);
 63     else if(f[i]==1) link(i+n,T,1);
 64     for(int j=i+1;j<=n;j++) {
 65         if(a[j]>=a[i] && f[j]+1==f[i]) link(i+n,j,1);
 66     }
 67     }
 68 }
 69 
 70 inline bool BFS(){
 71     while(!Q.empty()) Q.pop();
 72     memset(deep,0,sizeof(deep));
 73     Q.push(S); deep[S]=1;    
 74     while(!Q.empty()) {
 75     int u=Q.front(); Q.pop();
 76     for(int i=first[u];i;i=e[i].next) {
 77         int v=e[i].to;
 78         if(e[i].f && !deep[v]) deep[v]=deep[u]+1,Q.push(v);
 79     }
 80     }
 81     if(!deep[T]) return false;
 82     return true;
 83 }
 84 
 85 inline int maxflow(int x,int remain){
 86     if(x==T || remain==0) return remain;
 87     int flow=0,f;
 88     for(int i=first[x];i;i=e[i].next) {
 89     if(e[i].f && deep[e[i].to]==deep[x]+1) {
 90         int v=e[i].to;
 91         f=maxflow(v,min(remain,e[i].f));
 92         if(f) {
 93         flow+=f; e[i].f-=f; e[i^1].f+=f;
 94         remain-=f;
 95         if(remain==0) return flow;
 96         }else deep[v]=-1; 
 97     }
 98     }
 99     return flow;
100 }
101 
102 inline void build_new(){
103     memset(first,0,sizeof(first));
104     S=2*n+1; T=S+1;  ecnt=1;
105     for(int i=1;i<=n;i++) {
106     if(i==1 || i==n) {
107         link(i,i+n,inf);
108         if(f[i]==maxl) link(S,i,inf);
109         else if(f[i]==1) link(i+n,T,inf);
110     }
111     else{
112         link(i,i+n,1);
113         if(f[i]==maxl) link(S,i,1);
114         else if(f[i]==1) link(i+n,T,1);
115     }
116     for(int j=i+1;j<=n;j++) {
117         if(a[j]>=a[i] && f[j]+1==f[i]) link(i+n,j,1);
118     }
119     }
120 }
121 
122 inline void work(){
123     n=getint(); for(int i=1;i<=n;i++) a[i]=getint();
124     for(int i=n;i>=1;i--) {
125     f[i]=find(a[i])+1;
126     if(b[f[i]]==0) b[f[i]]=a[i];
127     else b[f[i]]=max(a[i],b[f[i]]);
128     maxl=max(maxl,f[i]);
129     }
130     printf("%d\n",maxl);
131 
132     build();
133     while(BFS()) ans+=maxflow(S,inf);
134     if(ans==0) ans=n;
135     printf("%d\n",ans);
136 
137     ans=0;
138     build_new();
139     while(BFS()) ans+=maxflow(S,inf);
140     if(ans==0) ans=n;
141     printf("%d\n",ans);
142 }
143 
144 int main()
145 {
146   work();
147   return 0;
148 }
149  

 

 
posted @ 2016-08-06 15:56  ljh_2000  阅读(261)  评论(0编辑  收藏  举报