zoukankan      html  css  js  c++  java
  • [网络流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;
    }
    
  • 相关阅读:
    使用 crontab 来定时执行脚本,无法执行,但是如果直接通过命令(如:./test.sh)又可以正常执行,
    解决RABBITMQ ADMIN账号登陆失败的问题
    Job for rabbitmq-server.service failed because the control process exited with error code. See "systemctl status rabbitmq-server.service" and "journalctl -xe" for details.
    git 删除远端分支,本地新创建分支推到远程或者 拉取远程分支并创建本地分支
    细品 Spring Boot+Thymeleaf,还有这么多好玩的细节!
    Spring Boot 日志各种使用姿势,是时候捋清楚了!
    SQL多节节点MS SQL中的递归
    支付DLL接口
    (转)ubuntu下查询CPU数,核心数,线程数
    (转)C/C++中计算程序运行时间
  • 原文地址:https://www.cnblogs.com/BruceW/p/12203463.html
Copyright © 2011-2022 走看看