[网络流24题]最长不下降子序列问题
题目:洛谷P2766、codevs1906。
题目大意:给你一个数列,要你回答:
1.它的最长不下降子序列;
2.在这个数列中,能取出多少种元素不重复(下标不同即可)的最长不下降子序列;
3.现在规定第一个元素和最后一个元素可以重复利用,问第二问。
解题思路:codevs题面有误,应该是不下降而不是递增。
第一问DP,$O(n^2)$求出f(f[i]表示以第i个元素结尾的最长不下降子序列长度)。
第二、三问都是网络流。
首先对每个元素拆成入点和出点(设分别为i和i+n),然后
对于f[i]=1的i,连接0(超级源点)和i(i的入点),容量1。
对于f[i]=n的i,连接2n(n的出点)和2n+1(超级汇点),容量为1。
然后,对于每一对i<j,若a[i]<=a[j]且f[i]+1=f[j],则连接i+n(i的出点)和j(j的入点),容量为1。
容量为1是因为每个元素只能使用一次。
跑网络流即是第二问的答案。
第三问呢?
由于两个点没有限制,所以把相关边的容量设为inf即可。
所以只要加上0到1,1到n+1的边,和n到2n,2n到2n+1的边(这两条仅当f[n]的值是最长不下降子序列长度时才添加),容量都为inf,
然后在残量网络中继续跑,最后和原来的答案加起来即可。(当然也可以重新构图)。
注意:当原数列严格递减时(即DP答案为1时),答案应该是1,n,n。此时网络流可能跑出问题,需要加特判。
C++ Code:
#include<cstdio>
#include<cstring>
#include<queue>
#define inf 0x3fffffff
std::queue<int>q;
int a[505],f[505],n,head[1007],cnt=0,level[1007],iter[1007];
struct edge{
int to,cap,nxt,rev;
}e[1005*1005*2];
inline void addedge(int u,int v,int flow){
++cnt;
e[cnt]=(edge){v,flow,head[u],cnt+1};
head[u]=cnt;
++cnt;
e[cnt]=(edge){u,0,head[v],cnt-1};
head[v]=cnt;
}
void bfs(int s){
level[s]=1;
q.push(s);
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=head[u];i!=-1;i=e[i].nxt)
if(level[e[i].to]<0&&e[i].cap){
level[e[i].to]=level[u]+1;
q.push(e[i].to);
}
}
}
int dfs(int u,int t,int f){
if(u==t)return f;
for(int& i=iter[u];i!=-1;i=e[i].nxt)
if(level[e[i].to]>level[u]&&e[i].cap){
int d=dfs(e[i].to,t,f>e[i].cap?e[i].cap:f);
if(d){
e[i].cap-=d;
e[e[i].rev].cap+=d;
return d;
}
}
return 0;
}
int maxflow(int s,int t){
for(int flow=0;;){
memset(level,-1,sizeof level);
bfs(s);
if(level[t]==-1)return flow;
memcpy(iter,head,sizeof iter);
int f=0;
while(f=dfs(s,t,inf))flow+=f;
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i)scanf("%d",&a[i]);
memset(f,0,sizeof f);
f[1]=1;
int ans1=1;
for(int i=2,j;i<=n;++i)
for(j=f[i]=1;j<i;++j)
if(a[j]<=a[i]&&f[j]+1>f[i]){
f[i]=f[j]+1;
if(ans1<f[i])ans1=f[i];
}
if(ans1==1)return!printf("%d\n%d\n%d\n",1,n,n);
printf("%d\n",ans1);
memset(head,-1,sizeof head);
for(int i=1;i<=n;++i){
addedge(i,i+n,1);
if(f[i]==ans1)addedge(i+n,n<<1|1,1);else{
if(f[i]==1)addedge(0,i,1);
for(int j=i+1;j<=n;++j)
if(a[i]<=a[j]&&f[i]+1==f[j])addedge(i+n,j,1);
}
}
int ans2;
printf("%d\n",ans2=maxflow(0,n<<1|1));
addedge(0,1,inf);
if(f[n]==ans1)addedge(n<<1,n<<1|1,inf),addedge(n,n<<1,inf);
addedge(1,n+1,inf);
printf("%d\n",ans2+maxflow(0,n<<1|1));
return 0;
}

浙公网安备 33010602011771号