[网络流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)\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;
}
posted @ 2020-01-16 21:58  BruceW  阅读(143)  评论(0编辑  收藏  举报