codevs1906 最长递增子序列问题
给定正整数序列x1,..... , xn 。
(1)计算其最长递增子序列的长度s。
(2)计算从给定的序列中最多可取出多少个长度为s的递增子序列。
(3)如果允许在取出的序列中多次使用x1和xn,则从给定序列中最多可取出多少个长
度为s的递增子序列。
第1 行有1个正整数n,表示给定序列的长度。接
下来的1 行有n个正整数x1.....xn 。
第1 行是最长递增子序列的长度s。第2行是可取出的长度为s 的递增子序列个数。第3行是允许在取出的序列中多次使用x1和xn时可取出的长度为s 的递增子序列个数。
4
3 6 2 5
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的边,内部的边,全部把容量改为无穷大就可以了。其余的和第二问没有区别。
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