zoukankan      html  css  js  c++  java
  • 「解题报告」[网络流24题] 16.数字梯形问题 (最大费用最大流)

    16.数字梯形问题

    题意

    有一个有数字组成的梯形,

    该梯形有 (n) 行, 第一行有 (m) 列, 每一行都比前一行多一列.

    (m) 条从梯形顶部到底部的路径, 分别以第一行的 (m) 个元素作为起点, 每次可以往左下或右下移动.

    分别回答满足以下三个条件时, (m) 条路径上数字总和的最大值.

    1. (m) 条路径互不相交.
    2. (m) 条路径可以点相交, 不可以边相交.
    3. (m) 条路径既可以点相交, 也可以边相交.

    思路

    这一道题其实就是 DAG 不相交路径的各种变式.

    第一问, 按照套路, 每个点只能出现一次, 那么就把每个点分为入点出点, 入点和出点之间连一条容量为 (1), 代价为 (0) 的边, 跑最大费用最大流就行了.

    第二问, 本来是不用建入点和出点的, 但我们为了避免重复建图, 可以直接把入点到出点的边容量改为 (inf), 其他的和原来一样. 需要注意的是, 不能修改了边权后直接在残量网络上跑最大流, 因为这时已经找不到增广路了.

    第三问, 一开始本来是想 (dp) 的....但是给出题人点面子...那么就把除了从源点 (S) 练出来的边以外, 所有边的边权都改为 (inf). (当然, 反向边除外).


    代码

    #include<bits/stdc++.h>
    using namespace std;
    const int _=60+7;
    const int __=1202+7;
    const int ___=3760+7;
    const int inf=0x3f3f3f3f;
    int n,m,S,T,a[_][_],num[_][_],dis[__],ans,cnt;
    int lst[__],nxt[___],to[___],c[___],w[___],bk[___],tot=1;
    bool vis[__],mst[___],cha[___];
    queue<int> q;
    void init(){
      cin>>m>>n;
      for(int i=1;i<=n;i++){
        for(int j=1;j<=m+i-1;j++){
          scanf("%d",&a[i][j]);
          num[i][j]=++cnt;
        }
      }
      S=(m+m+n-1)*n+1; T=S+1;
    }
    void add(int x,int y,int cap,int wgt){
      if(x==S) mst[tot+1]=1;
      if(y==x+cnt||y==T) cha[tot+1]=1;
      nxt[++tot]=lst[x]; to[tot]=y; c[tot]=cap; w[tot]=wgt; lst[x]=tot; bk[tot]=cap;
      nxt[++tot]=lst[y]; to[tot]=x; c[tot]=0; w[tot]=-wgt; lst[y]=tot;
    }
    void link(){
      for(int i=1;i<=m;i++)
        add(S,num[1][i],1,a[1][i]);
      for(int i=1;i<=m+n-1;i++)
        add(num[n][i]+cnt,T,1,0);
      for(int i=1;i<n;i++)
        for(int j=1;j<=m+i-1;j++){
          add(num[i][j]+cnt,num[i+1][j],1,a[i+1][j]);
          add(num[i][j]+cnt,num[i+1][j+1],1,a[i+1][j+1]);
        }
      for(int i=1;i<=cnt;i++)
        add(i,i+cnt,1,0);
    }
    bool SPFA(){
      for(int i=1;i<=T;i++){ dis[i]=-inf; vis[i]=0; }
      while(!q.empty()) q.pop();
      dis[S]=0; q.push(S);
      while(!q.empty()){
        int u=q.front(); q.pop();
        vis[u]=0;
        for(int i=lst[u];i;i=nxt[i]){
          int v=to[i];
          if(!c[i]||dis[v]>=dis[u]+w[i]) continue;
          dis[v]=dis[u]+w[i];
          if(!vis[v]){
    	q.push(v);
    	vis[v]=1;
          }
        }
      }
      return dis[T]!=-inf;
    }
    int dfs(int u,int flow){
      if(u==T) return flow;
      int rest=flow; vis[u]=1;
      for(int i=lst[u];i;i=nxt[i]){
        int v=to[i];
        if(vis[v]||!c[i]||dis[v]!=dis[u]+w[i]) continue;
        int cst=dfs(v,min(rest,c[i]));
        if(cst==0) dis[v]=-inf;
        else{
          c[i]-=cst; c[i^1]+=cst;
          rest-=cst; ans+=cst*w[i];
        }
      }
      return flow-rest;
    }
    void Dinic(){
      ans=0;
      int flow;
      while(SPFA())
        do{
          flow=dfs(S,inf);
        }while(flow);
    }
    void change(int ty){
      if(ty==2){
        for(int i=2;i<=tot;i+=2){
          if(cha[i]) c[i]=inf;
          else c[i]=bk[i];
          c[i^1]=0;
        }
      }
      else{
        for(int i=2;i<=tot;i+=2){
          if(!mst[i]) c[i]=inf;
          else c[i]=bk[i];
          c[i^1]=0;
        }
      }
    }
    int main(){
    #ifndef ONLINE_JUDGE
      freopen("x.in","r",stdin);
    #endif
      init();
      link();
      Dinic();
      printf("%d
    ",ans);
      
      change(2);
      Dinic();
      printf("%d
    ",ans);
      
      change(3);
      Dinic();
      printf("%d
    ",ans);
      
      return 0;
    }
    
  • 相关阅读:
    Oracle手工增加排序区避免SQL使用临时表空间排序产生物理IO
    Oracle中"TABLE ACCESS FULL"的”欺骗时刻“
    Oracle关于12C新特性InMemory踩坑历程
    Oracle19C关于参数sec_case_sensitive_logon控制密码大小写敏感问题
    友链
    RESTful API
    不自由的自由职业
    惊了!修仙=编程??
    [Git专题] 环境搭建
    Linux系统僵尸进程详解
  • 原文地址:https://www.cnblogs.com/BruceW/p/12207742.html
Copyright © 2011-2022 走看看