zoukankan      html  css  js  c++  java
  • 【网络流】——最小费用最大流EK算法

    本文非原创,部分参考自秋日私语大大的题解。

    GO:模板


      最大流问题,我们可以通过EK,DINIC等方法解决,但是如果在每条边上加上边权,并定义 费用 为边权乘以流量,在所有最大流中求出费用最少的那一个方案,就需要新的算法了。

      这里只对最小费用最大流的EK-SPFA算法做一些自己的解释,并会附上模板。

    首先是EK算法:

      1.我们每次求一条源点到汇点的路径,流量最大为路径上的最小流量minflow,所以下一次寻找最大流就不能走那条,于是将所有边的流量减小minflow。

      2.由1能保证求出最大流吗?答案是否定的,因为当我们遇到一些特殊的图时:

      比如这个,第一条路径S-2-T,边上流量变为0,得到maxflow=4,实际上maxflow=6,因此我们要加一条反向边让路径有“反悔”的机会。

      3.我们使用bfs进行增广,这样能保证在更少的次数下求出最大流。

      4.对2具体的操作:记录路径上每个点的前驱和前驱边,走完spfa后从后向前遍历前驱边,修改流量。

    最小费用最大流:

      众所周知,SPFA他死了。

      但这并不影响我们做网络流的题目(出题人没必要在网络流的题目上用图论卡我们),我们借鉴spfa的思想,对路径上求最短路。

      具体看代码。

     1 //EK-MCMF
     2 #include<bits/stdc++.h>
     3 using namespace std;
     4 const int inf=1e9;
     5 #define f(i,a,b) for(register int i=a;i<=b;i++)
     6 const int N=5e4+10;
     7 int head[N<<1],flow[N],l[N],dis[N],pre[N],vis[N],cnt=-1,n,m,s,t,ansf,ansc;
     8 //flow判断路径最小流量,dis判断最短路,pre是前驱点,l是前驱边,vis判断是否在队列中 
     9 queue<int> q;
    10 struct edge{int to,next,f,w;}e[N<<1];
    11 void addedge(int from,int to,int f,int w){e[++cnt]=(edge){to,head[from],f,w}; head[from]=cnt;}
    12 bool spfa(){
    13     memset(dis,0x7f,sizeof(dis));
    14     memset(vis,0,sizeof(vis));
    15     vis[s]=1;
    16     dis[s]=pre[t]=0;
    17     flow[s]=inf;
    18     q.push(s);
    19     while(!q.empty()){
    20         int u=q.front();
    21         vis[u]=0;
    22         for(int i=head[u];i!=-1;i=e[i].next){
    23             int v=e[i].to,w=e[i].w,f=e[i].f;
    24             if(f>0&&dis[v]>dis[u]+w){
    25                 dis[v]=dis[u]+w;
    26                 pre[v]=u;
    27                 l[v]=i;
    28                 flow[v]=min(flow[u],f);
    29                 if(!vis[v]){
    30                     q.push(v);
    31                     vis[v]=1;
    32                 }
    33             }
    34         }
    35         q.pop();
    36     }
    37     return pre[t];
    38 }
    39 void solve(){
    40     while(spfa()){
    41         ansf+=flow[t];
    42         ansc+=dis[t]*flow[t];
    43         for(int i=t;i!=s;i=pre[i]){
    44             int edge_=l[i];
    45             e[edge_].f-=flow[t];
    46             e[edge_^1].f+=flow[t];
    47         }
    48     }
    49     printf("%d %d",ansf,ansc);
    50 }
    51 int main(){
    52     memset(head,-1,sizeof(head));
    53     scanf("%d%d%d%d",&n,&m,&s,&t);
    54     int x,y,f,w;
    55     f(i,1,m){
    56         scanf("%d%d%d%d",&x,&y,&f,&w);
    57         addedge(x,y,f,w);
    58         addedge(y,x,0,-w);
    59     }
    60     solve();
    61     return 0;
    62 }

      

    ——抓住了时间,却不会利用的人,终究也逃不过失败的命运。
  • 相关阅读:
    jQuery之设置元素内容(移动和复制元素,使用append(),appendTo()方法)
    如何使用CSS隐藏滚动条并且兼容大部分浏览器
    contentType和dataType
    JavaScript获取客户端IP地址
    IMG标签与before,after伪类
    转载《学习HTML5 canvas遇到的问题》
    如何禁用向窗口拖放文件
    等高响应式布局的原理和实现
    高德网络定位之“移动WiFi识别”
    Android Native 内存泄漏系统化解决方案
  • 原文地址:https://www.cnblogs.com/Nelson992770019/p/11336039.html
Copyright © 2011-2022 走看看