[网络流24题] 最长递增子序列
«问题描述:
给定正整数序列x1,..., xn。
(1)计算其最长递增子序列的长度s。
(2)计算从给定的序列中最多可取出多少个长度为s的递增子序列。
(3)如果允许在取出的序列中多次使用x1和xn,则从给定序列中最多可取出多少个长
度为s的递增子序列。
注意:这里的最长递增子序列即最长不下降子序列!!!
«编程任务:
设计有效算法完成(1)(2)(3)提出的计算任务。
«数据输入:
由文件alis.in提供输入数据。文件第1 行有1个正整数n(n<=500),表示给定序列的长度。接
下来的1 行有n个正整数x1,..., xn。
«结果输出:
程序运行结束时,将任务(1)(2)(3)的解答输出到文件alis.out中。第1 行是最长
递增子序列的长度s。第2行是可取出的长度为s 的递增子序列个数。第3行是允许在取出
的序列中多次使用x1和xn时可取出的长度为s 的递增子序列个数。
输入文件示例 输出文件示例
alis.in
4
3 6 2 5
alis.out
2
2
3
题目描述的不是很清楚,不是递增,是不下降,另外无限用事实上指的是1和n可以被用于多个不下降序列中,可以重复使用,而其他点只可以用一次。
对于第一问,随便求一下就行...
对于后两问,我们想到网络流的方法(毕竟网络流24题),如何限制一个点的经过次数呢?我们可以把它拆成两个点,在两个点之间连一条权值为x的边,表示这个点最多经过x次。那如何建图呢,对于两个位置i,j,如果a[i]<=a[j]并且i<j并且g[i]+1=g[j],那么我们就可以在他们之间连一条边,想一下也很简单。那么与源点(S)和汇点(T)如何连边呢?
对于第二问的情况,连1就行了,因为最多用一次。
对于第三问,非无限取的点和第二问一样,对于无限取的点,他与S相连的权值至少应该为他可能作为起点出现的次数a,与T相连的权值至少应该为他可能作为终点出现的次数b。对于只与S或者只与T相连的点,这种点的存在说明了最长不下降子序列长度不为1,我们边的权值可以取任意大于等于a或者b数字,因为其他的点会对他做出限制,他取大一点也没关系,反正也流不了那么多,但对于s与t均连在一个点上的情况,说明最长不下降序列长度为1,这时候a,b中比较小的那一个(其实这种情况全是1),就必须取他本身,也就是1,才能“限制”住,不然的话他的流量就变成inf了。而1这个点又比较特殊,如果他可以作为终点出现,那么说明最长不下降序列长度为1,也就一定可以作为起点出现,那么1和t的连边权值一定是1,n点同理,与s连边权值一定是1,。
语文比较差,,,可能讲不清楚。也可能讲的就是错的qwq,因为我yy了一下午也就很牵强的说服了自己。。。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int inf=1e6;//随便开... 4 int n,a[inf],f[inf],g[inf],top; 5 int tot,fi[inf],to[inf],next[inf],cost[inf],rev[inf]; 6 int ans,que[inf],head,tail,dep[inf],cur[inf]; 7 void slove1(){ 8 for(int i=1;i<=n;i++){ 9 if(a[i]>=f[top])f[++top]=a[i],g[i]=top; 10 else { 11 int l=1,r=top; 12 while(l!=r){ 13 int mid=(l+r)>>1; 14 if(f[mid]<=a[i])l=mid+1; 15 else r=mid; 16 } 17 f[l]=a[i]; 18 g[i]=l; 19 } 20 } 21 printf("%d\n",top); 22 } 23 void edge_add(int x,int y,int z){ 24 to[++tot]=y;next[tot]=fi[x]; 25 fi[x]=tot;cost[tot]=z;rev[tot]=tot+1; 26 to[++tot]=x;next[tot]=fi[y]; 27 fi[y]=tot;cost[tot]=0;rev[tot]=tot-1; 28 } 29 bool bfs(){ 30 for(int i=1;i<=n*2+2;i++)cur[i]=fi[i],dep[i]=-1; 31 dep[1]=0; 32 head=1;tail=0; 33 que[++tail]=1; 34 while(head<=tail){ 35 int u=que[head++]; 36 for(int i=fi[u];i;i=next[i]) 37 if(cost[i]&&dep[to[i]]==-1) 38 dep[to[i]]=dep[u]+1, 39 que[++tail]=to[i]; 40 } 41 return dep[n*2+2]!=-1; 42 } 43 int dfs(int x,int f){ 44 if(x==n*2+2)return f; 45 for(int i=cur[x];i;i=next[i]){ 46 cur[x]=i; 47 if(cost[i]&&dep[to[i]]==dep[x]+1){ 48 int g=dfs(to[i],min(f,cost[i])); 49 if(g){ 50 cost[i]-=g; 51 cost[rev[i]]+=g; 52 return g; 53 } 54 } 55 } 56 return 0; 57 } 58 void dinic(){ 59 ans=0; 60 int f; 61 while(bfs()) 62 while(f=dfs(1,0x3fffffff))ans+=f; 63 printf("%d\n",ans); 64 } 65 int main() 66 { 67 freopen("alis.in","r",stdin); 68 freopen("alis.out","w",stdout); 69 // freopen("1.txt","r",stdin); 70 scanf("%d",&n); 71 for(int i=1;i<=n;i++)scanf("%d",&a[i]); 72 slove1(); 73 for(int i=1;i<=n;i++){ 74 edge_add(i<<1,i<<1|1,1); 75 if(g[i]==1)edge_add(1,i<<1,1); 76 if(g[i]==top)edge_add(i<<1|1,n*2+2,1); 77 } 78 for(int i=1;i<n;i++) 79 for(int j=i+1;j<=n;j++) 80 if(a[i]<=a[j]&&g[i]+1==g[j]) 81 edge_add(i<<1|1,j<<1,1); 82 dinic(); 83 tot=0; 84 memset(fi,0,sizeof(fi)); 85 memset(to,0,sizeof(to)); 86 memset(next,0,sizeof(next)); 87 memset(rev,0,sizeof(rev)); 88 memset(cost,0,sizeof(cost)); 89 for(int i=1;i<=n;i++){ 90 edge_add(i<<1,i<<1|1,1); 91 if(g[i]==1)edge_add(1,i<<1,1); 92 if(g[i]==top)edge_add(i<<1|1,n*2+2,1); 93 } 94 for(int i=1;i<n;i++) 95 for(int j=i+1;j<=n;j++) 96 if(a[i]<=a[j]&&g[i]+1==g[j]) 97 edge_add(i<<1|1,j<<1,1); 98 edge_add(2,3,0x3fffffff);edge_add(n<<1,n<<1|1,0x3fffffff); 99 if(g[1]==1)edge_add(1,2,0x3fffffff); 100 if(g[n]==top)edge_add(n<<1,n*2+2,0x3fffffff); 101 dinic(); 102 return 0; 103 }