[网络流24题] 6.最长不下降子序列问题 解题报告

6.最长不下降子序列问题

题意

给定正整数序列 (x_1, x_2,dots, x_n). ((n le 500))

求:

  1. 该序列的最长不下降子序列长度.
  2. 设第一问的答案为 (k), 求从该序列中最多能拿出多少长度为 (k) 的不下降子序列. (即, 点不能重复)
  3. (x_1,x_n) 可以无限次使用, 求从该序列中最多能拿出多少长度为 (k)不同的不下降子序列.

思路

首先 (n^2) 做一遍 (dp), 求出 (lgt[i]), 表示以 (i) 结尾的最长不下降子序列长度, 设第一问的答案为 (ans1).

(S) 为源点, (T) 为汇点, 在点之间连边, 一共有 3 种边. (注 : ((x,y,c)) 表示从 (x)(y) 连一条容量为 (c) 的边)

  1. (lgt[i]=1), 连 ((S,i,1),(i,S,0)).
  2. (lgt[i]=ans1), 连 ((i,T,1),(T,i,0)).
  3. (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)
",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
",ans1);
  link();
  Dinic();
  printf("%d
",max_flow);
  c[e1]=c[e2]=c[e3]=c[e4]=inf;
  Dinic();
  printf("%d
",max_flow);
  return 0;
}
原文地址:https://www.cnblogs.com/BruceW/p/12203463.html