[网络流24题] 6.最长不下降子序列问题 解题报告
题意
给定正整数序列 \(x_1, x_2,\dots, x_n\). \((n \le 500)\)
求:
- 该序列的最长不下降子序列长度.
- 设第一问的答案为 \(k\), 求从该序列中最多能拿出多少长度为 \(k\) 的不下降子序列. (即, 点不能重复)
- 若 \(x_1,x_n\) 可以无限次使用, 求从该序列中最多能拿出多少长度为 \(k\) 的不同的不下降子序列.
思路
首先 \(n^2\) 做一遍 \(dp\), 求出 \(lgt[i]\), 表示以 \(i\) 结尾的最长不下降子序列长度, 设第一问的答案为 \(ans1\).
设 \(S\) 为源点, \(T\) 为汇点, 在点之间连边, 一共有 3 种边. (注 : \((x,y,c)\) 表示从 \(x\) 向 \(y\) 连一条容量为 \(c\) 的边)
- 若 \(lgt[i]=1\), 连 \((S,i,1),(i,S,0)\).
- 若 \(lgt[i]=ans1\), 连 \((i,T,1),(T,i,0)\).
- 若 \(j \le i,\ x_j \le x_i\) 且 \(lgt[i]=lgt[j]+1\), 连 \((j,i,1),(i,j,0)\).
相当于是把刚才 \(dp\) 过程中的关系用一张图表示出来了, 在这张图上跑一边最大流, 求出的最大流即为第二问的答案.
还需要注意的是, 由于每个点不能重复选, 所以我们把每个点都分为 入点 和 出点, 由入点向出点连一条容量为 \(1\) 的边, 就能够保证每个点只被选一遍.
第三问的话把 \((S,1),(n,T)\) 以及 \(1\) 和 \(n\) 出点和入点之间的边容量改为 \(inf\), 在残量网络上再跑一遍最大流即可.
代码
#include<bits/stdc++.h>
using namespace std;
const int _=1100+7;
const int __=504000+7;
const int inf=0x3f3f3f3f;
int n,m,S,T,a[_],d[_],mp[_][_],lgt[_],ans1,max_flow,e1,e2,e3,e4;
int lst[_],nxt[__],to[__],c[__],tot=1;
queue<int> q;
void pre(){
cin>>n; S=2*n+1; T=2*n+2;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
lgt[i]=1;
}
for(int i=1;i<=n;i++){
for(int j=1;j<i;j++)
if(a[i]>=a[j]) lgt[i]=max(lgt[i],lgt[j]+1);
ans1=max(ans1,lgt[i]);
}
}
void add(int x,int y,int cap){
//printf("(%d,%d,%d)\n",x,y,cap);
if(x==S&&y==1) e1=tot+1;
if(x==1&&y==1+n) e2=tot+1;
if(x==n+n&&y==T) e3=tot+1;
if(x==n&&y==n+n) e4=tot+1;
nxt[++tot]=lst[x]; to[tot]=y; c[tot]=cap; lst[x]=tot;
nxt[++tot]=lst[y]; to[tot]=x; c[tot]=0; lst[y]=tot;
}
void link(){
for(int i=n;i>=1;i--){
add(i,i+n,1);
for(int j=1;j<i;j++){
if(a[i]>=a[j]&&lgt[i]==lgt[j]+1) add(j+n,i,1);
}
if(lgt[i]==1) add(S,i,1);
if(lgt[i]==ans1) add(i+n,T,1);
}
}
bool bfs(){
memset(d,0,sizeof(d));
while(!q.empty()) q.pop();
d[S]=1; q.push(S);
while(!q.empty()){
int u=q.front(); q.pop();
for(int i=lst[u];i;i=nxt[i]){
int v=to[i];
if(d[v]||!c[i]) continue;
d[v]=d[u]+1;
if(v==T) return 1;
q.push(v);
}
}
return 0;
}
int dfs(int u,int flow){
if(u==T) return flow;
int rest=flow;
for(int i=lst[u];i;i=nxt[i]){
int v=to[i];
if(d[v]!=d[u]+1||!c[i]) continue;
int cost=dfs(v,min(rest,c[i]));
if(cost==0) d[v]=0;
else{
c[i]-=cost; c[i^1]+=cost;
rest-=cost;
}
}
return flow-rest;
}
void Dinic(){
int flow;
while(bfs())
do{
flow=dfs(S,inf);
max_flow+=flow;
}while(flow);
}
int main(){
#ifndef ONLINE_JUDGE
freopen("x.in","r",stdin);
#endif
pre();
printf("%d\n",ans1);
link();
Dinic();
printf("%d\n",max_flow);
c[e1]=c[e2]=c[e3]=c[e4]=inf;
Dinic();
printf("%d\n",max_flow);
return 0;
}