网络流的最大流和最小流是啥算法

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络流的最大流和最小流是啥算法相关的知识,希望对你有一定的参考价值。

参考技术A 首先是网络流中的一些定义:
V表示整个图中的所有结点的集合.
E表示整个图中所有边的集合.
G = (V,E) ,表示整个图.
s表示网络的源点,t表示网络的汇点.
对于每条边(u,v),有一个容量c(u,v) (c(u,v)>=0),如果c(u,v)=0,则表示(u,v)不存在在网络中。相反,如果原网络中不存在边(u,v),则令c(u,v)=0.
对于每条边(u,v),有一个流量f(u,v).

一个简单的例子.网络可以被想象成一些输水的管道.括号内右边的数字表示管道的容量c,左边的数字表示这条管道的当前流量f.

网络流的三个性质:
1、容量限制: f[u,v]<=c[u,v]
2、反对称性:f[u,v] = - f[v,u]
3、流量平衡: 对于不是源点也不是汇点的任意结点,流入该结点的流量和等于流出该结点的流量和。
只要满足这三个性质,就是一个合法的网络流.
最大流问题,就是求在满足网络流性质的情况下,源点 s 到汇点 t 的最大流量。

求一个网络流的最大流有很多算法 这里首先介绍 增广路算法(EK)
学习算法之前首先看了解这个算法中涉及到的几个图中的定义:

**残量网络
为了更方便算法的实现,一般根据原网络定义一个残量网络。其中r(u,v)为残量网络的容量。
r(u,v) = c(u,v) – f(u,v)
通俗地讲:就是对于某一条边(也称弧),还能再有多少流量经过。
Gf 残量网络,Ef 表示残量网络的边集.

这是上面图的一个残量网络。残量网络(如果网络中一条边的容量为0,则认为这条边不在残量网络中。
r(s,v1)=0,所以就不画出来了。另外举个例子:r(v1,s) = c(v1,s) – f(v1,s) = 0 – (-f(s,v1)) = f(s,v1) = 4.
其中像(v1,s)这样的边称为后向弧,它表示从v1到s还可以增加4单位的流量。
但是从v1到s不是和原网络中的弧的方向相反吗?显然“从v1到s还可以增加4单位流量”这条信息毫无意义。那么,有必要建立这些后向弧吗?
显然,第1个图中的画出来的不是一个最大流。
但是,如果我们把s -> v2 -> v1 -> t这条路径经过的弧的流量都增加2,就得到了该网络的最大流。
注意到这条路径经过了一条后向弧:(v2,v1)。
如果不设立后向弧,算法就不能发现这条路径。
**从本质上说,后向弧为算法纠正自己所犯的错误提供了可能性,它允许算法取消先前的错误的行为(让2单位的流从v1流到v2)

注意,后向弧只是概念上的,在程序中后向弧与前向弧并无区别.

**增广路
增广路定义:在残量网络中的一条从s通往t的路径,其中任意一条弧(u,v),都有r[u,v]>0。

如图绿色的即为一条增广路。

看了这么多概念相信大家对增广路算法已经有大概的思路了吧。

**增广路算法
增广路算法:每次用BFS找一条最短的增广路径,然后沿着这条路径修改流量值(实际修改的是残量网络的边权)。当没有增广路时,算法停止,此时的流就是最大流。

**增广路算法的效率
设n = |V|, m = |E|
每次增广都是一次BFS,效率为O(m),而在最坏的情况下需要(n-2增广。(即除源点和汇点外其他点都没有连通,所有点都只和s与t连通)
所以,总共的时间复杂度为O(m*n),所以在稀疏图中效率还是比较高的。

hdoj 1532是一道可以作为模板题目练手。
模板代码:

[cpp] view plain copy print?
#include <cstdio>
#include <cstring>
#include <iostream>
#include <string>
#include <algorithm>
#include <map>
#include <vector>
using namespace std;
const int N = 1100;
const int INF = 0x3f3f3f3f;

struct Node

int to;//终点
int cap; //容量
int rev; //反向边
;

vector<Node> v[N];
bool used[N];

void add_Node(int from,int to,int cap) //重边情况不影响

v[from].push_back((Node)to,cap,v[to].size());
v[to].push_back((Node)from,0,v[from].size()-1);


int dfs(int s,int t,int f)

if(s==t)
return f;
used[s]=true;
for(int i=0;i<v[s].size();i++)

Node &tmp = v[s][i]; //注意
if(used[tmp.to]==false && tmp.cap>0)

int d=dfs(tmp.to,t,min(f,tmp.cap));
if(d>0)

tmp.cap-=d;
v[tmp.to][tmp.rev].cap+=d;
return d;



return 0;


int max_flow(int s,int t)

int flow=0;
for(;;)
memset(used,false,sizeof(used));
int f=dfs(s,t,INF);
if(f==0)
return flow;
flow+=f;


int main()

int n,m;
while(~scanf("%d%d",&n,&m))

memset(v,0,sizeof(v));
for(int i=0;i<n;i++)

int x,y,z;
scanf("%d%d%d",&x,&y,&z);
add_Node(x,y,z);

printf("%d\n",max_flow(1,m));

本回答被提问者采纳

模板最小费用最大流

洛咕

题意:如题,给出一个网络图,以及其源点和汇点,每条边已知其最大流量和单位流量费用,求出其网络最大流和在最大流情况下的最小费用.(N<=5000,M<=50000.)

分析:在(EK)算法求解最大流的基础上,把"用BFS任意寻找一条增广路"改为"用(SPFA)寻找一条单位流量费用之和最小的增广路"(也就是把(w(x,y))当做边权,在残量网络上求最短路)即可求出最小费用最大流.

注意:一条反向边((y,x))的费用应设为(-w(x,y)).

最大费用最大流就把最短路改成最长路即可.

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
inline int read(){
    int x=0,o=1;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')o=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*o;
}
const int N=5005;
const int M=100005;
int n,m,s,t,max_flow,ans;
int dis[N],visit[N],incf[N],pre[N];
int tot=1,head[N],nxt[M],to[M],limit[M],w[M];
inline void add(int a,int b,int c,int d){
    nxt[++tot]=head[a];head[a]=tot;
    to[tot]=b;limit[tot]=c;w[tot]=d;
}
inline bool spfa(){
    for(int i=1;i<=n;++i)visit[i]=0,dis[i]=1e9;
    queue<int>q;q.push(s);
    dis[s]=0;visit[s]=1;incf[s]=1e9;
    while(q.size()){
        int u=q.front();q.pop();visit[u]=0;
        for(int i=head[u];i;i=nxt[i]){
            if(!limit[i])continue;
            int v=to[i];
            if(dis[v]>dis[u]+w[i]){
                dis[v]=dis[u]+w[i];
                incf[v]=min(incf[u],limit[i]);
                pre[v]=i;
                if(!visit[v])visit[v]=1,q.push(v);
            }
        }
    }
    if(dis[t]==1e9)return false;
    return true;
}
inline void update(){
    int x=t;
    while(x!=s){
        int i=pre[x];
        limit[i]-=incf[t];
        limit[i^1]+=incf[t];
        x=to[i^1];
    }
    max_flow+=incf[t];
    ans+=dis[t]*incf[t];
}
int main(){
    n=read();m=read();s=read();t=read();
    for(int i=1;i<=m;++i){
        int a=read(),b=read(),w=read(),v=read();
        add(a,b,w,v);add(b,a,0,-v);
    }
    while(spfa())update();
    printf("%d %d
",max_flow,ans);
    return 0;
}

以上是关于网络流的最大流和最小流是啥算法的主要内容,如果未能解决你的问题,请参考以下文章

算法学习笔记(8.2): 上下界网络流

网络流的C++代码实现与过程讲解

km算法

初步网络最大流的算法及详解

模板最小费用最大流

标准输入流和输出流分别是啥,高效字符流的方法