前提:

我们想象一下自来水厂到你家的水管网是一个复杂的有向图,每一节水管都有一个最大承载流量。自来水厂不放水,你家就断水了。但是就算自来水厂拼命的往管网里面注水,你家收到的水流量也是上限(毕竟每根水管承载量有限)。你想知道你能够拿到多少水,这就是一种网络流问题。

在网上找了很久资料,虽然讲解网络流的资料很多但是浅显易懂的很少(可能是我太蒻了吧),写这篇文章只希望点进来的人都能学会网络流(都能点赞)

首先

最大流:

何为最大流 简单来说就是水流从一个源点s通过很多路径,经过很多点,到达汇点t,问你最多能有多少水能够到达t点。

结合图示理解: 从s到t经过若干个点,若干条边,每一条边的水流都不能超过边权值(可以小于等于但不能大于),所以该图的最大流就是10+22+45=77。 如果你还是不能理解,我们就换一种说法,假设s城有inf个人想去t城,但是从s到t要经过一些城市才能到达,(以上图为例)其中s到3城的火车票还剩10张,3到t的火车票还剩15张,其他路以此类推,问最终最多能有多少人能到达t城?

EK:

Edmond—Karp

增广路:

增广路: 增广路是指从s到t的一条路,流过这条路,使得当前的流(可以到达t的人)可以增加。 那么求最大流问题可以转换为不断求解增广路的问题,并且,显然当图中不存在增广路时就达到了最大流。 具体怎么操作呢? 其实很简单,直接从s到t广搜即可,从s开始不断向外广搜,通过权值大于0的边(因为后面会减边权值,所以可能存在边权为0的边),直到找到t为止,然后找到该路径上边权最小的边,记为mi,然后最大流加mi,然后把该路径上的每一条边的边权减去mi,直到找不到一条增广路(从s到t的一条路径)为止。(为什么要用mi呢?你要争取在这条路上多走更多人,但又不能让人停在某个城市)

代码:

#include<cstdio>
#include<cstdlib>
#include<queue>
#include<iostream>
using namespace std;
const int inf = 2147483647;
const int MAXN = 100100;
int head[MAXN],cnt = 1,low[MAXN],pre[MAXN],n,m,S,T;
int maxflow;
bool v[MAXN];
inline int read(){
int res = 0; char ch = getchar(); bool bo = false;
while(ch < '0' || ch > '9') bo = (ch == '-'), ch = getchar();
while(ch >= '0' && ch <= '9') res = (res << 1) + (res << 3) + (ch ^ 48), ch = getchar();
return bo ? -res : res;
}
struct node{int nxt,to,dis;}e[MAXN<<1];
void add(int from,int to,int dis)
{
e[++cnt]= (node){head[from],to,dis};
head[from]=cnt;
}
void EK()
{
int x=T;
while(x!=S)
{
int i=pre[x];
e[i].dis-=low[T];
e[i^1].dis+=low[T];
x=e[i^1].to;
}
maxflow += low[T];
}
queue <int> q;
bool bfs()
{
for(int i=1;i<=n;i++)v[i]=0;
while(q.size()) q.pop();
v[S]=1;
q.push(S);
low[S]=inf;
while(q.size())
{
int x=q.front();
q.pop();
for(int i=head[x];i;i=e[i].nxt)
{
if(e[i].dis > 0)
{
int y=e[i].to;
if(v[y])continue;
low[y]=min(low[x],e[i].dis);
pre[y]=i;
q.push(y);v[y]=1;
if(y==T)return true;
}
}
}
return false;
}
int main()
{
n=read();m=read();S=read();T=read();
int x,y,c;
for(int i=1;i<=m;i++)
{
x=read();y=read();c=read();
add(x,y,c); add(y,x,0);
}
while(bfs()) EK();
printf("%d\n",maxflow);
return 0;
}

  

Dinic:

Dinic算法分为两个步骤:

  1. bfs分层(在EK中bfs是用于寻找增广路的)
  2. dfs增广(dfs?EK中貌似没有这玩意啊,确定能高效?)
  3. 咦!刚才不是说两个步骤吗?重复执行1.2.直到图中无增广路为止

什么意思呢?

与EK一样,我们仍要通过bfs来判断图中是否还存在增广路,但是DInic算法里的bfs略有不同,这次,我们不用记录路径,而是给每一个点分层,对于任意点i,从s到i每多走过一个点,就让层数多一。

其实每次只找层数大一的,认为找最短增广路,为神魔呢:

有了分层,我们就不会选s->1->2->4->5->3->t了

刚才说了的,分完层下一步是dfs增广。

在Dinic中,我们找增广路是用深搜:

代码:

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
const int N = 100100;
const int inf = 214748347;
int cnt=1,head[10010],d[10010],n,m,s,t,maxflow;
bool v[N];
struct node{int nxt,to,dis;}e[N<<1];
void add(int from,int to,int dis)
{
e[++cnt] = (node){head[from],to,dis};
head[from]=cnt;
} queue <int> q;
bool bfs()
{
memset(d,0,sizeof d);
while(q.size())q.pop();
q.push(s);
d[s]=1;
while(q.size())
{
int x=q.front();q.pop();
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(e[i].dis && !d[y])
{
q.push(y);
d[y]=d[x]+1;
if(e[i].to==t)return 1;
}
}
}
return 0;
}
int dinic(int x,int flow)
{
if(x==t)return flow;
int rest=flow,k;
for(int i=head[x];i&&rest;i=e[i].nxt)
{
int y=e[i].to;
if(e[i].dis&&d[y]==d[x]+1)
{
k=dinic(y,min(rest,e[i].dis));
if(!k) d[y]=0;
e[i].dis-=k;
e[i^1].dis+=k;
rest-=k;
}
}
return flow-rest;
}
int main()
{
scanf("%d%d%d%d",&n,&m,&s,&t);
int x,y,c;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&c);
add(x,y,c);add(y,x,0);
}
int flow=0;
while(bfs())
{
while(flow=dinic(s,inf)) maxflow+=flow;
}
printf("%d\n",maxflow);
return 0;
}

  

还有一种超强的优化:当前弧(边)优化:

我们定义一个数组cur记录当前边(弧)(功能类比邻接表中的head数组,只是会随着dfs的进行而修改),

每次我们找过某条边(弧)时,修改cur数组,改成该边(弧)的编号,

那么下次到达该点时,会直接从cur对应的边开始(也就是说从head到cur中间的那一些边(弧)我们就不走了)。

有点抽象啊,感觉并不能加快,然而实际上确实快了很多。

代码:

bool bfs()
{
for(int i=1;i<=n;i++)
{
cur[i]=head[i];///////////////////只修改这几处,让你的代码飞快,相当于节省了dfs
          d[i]=0;///////中的链式前向星,因为head【】的边有的已经在前面使用
}
while(q.size())q.pop();
q.push(s);
d[s]=1;
while(q.size())
{
int x=q.front();q.pop();
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(e[i].dis && !d[y])
{
q.push(y);
d[y]=d[x]+1;
if(e[i].to==t)return 1;
}
}
}
return 0;
}
int dinic(int x,int flow)
{
if(x==t)return flow;
int rest=flow,k;
for(int i=cur[x];i&&rest;i=e[i].nxt)//////////////////
{
cur[x]=i;/////////////////
int y=e[i].to;
if(e[i].dis&&d[y]==d[x]+1)
{
k=dinic(y,min(rest,e[i].dis));
if(!k) d[y]=0;
e[i].dis-=k;
e[i^1].dis+=k;
rest-=k;
}
}
return flow-rest;
}

  

感谢__wfx 一下午的讲解,自己明白了很多

!!!!!!!!!!!!!!!!!!

缘来是你

透彻网络流-wfx-最大流的更多相关文章

  1. hdu 4940 Destroy Transportation system( 无源汇上下界网络流的可行流推断 )

    题意:有n个点和m条有向边构成的网络.每条边有两个花费: d:毁坏这条边的花费 b:重建一条双向边的花费 寻找这样两个点集,使得点集s到点集t满足 毁坏全部S到T的路径的费用和 > 毁坏全部T到 ...

  2. 【Luogu2711】小行星(网络流,最大流)

    [Luogu2711]小行星(网络流,最大流) 题面 题目描述 星云中有n颗行星,每颗行星的位置是(x,y,z).每次可以消除一个面(即x,y或z坐标相等)的行星,但是由于时间有限,求消除这些行星的最 ...

  3. 网络流之最大流Dinic算法模版

    /* 网络流之最大流Dinic算法模版 */ #include <cstring> #include <cstdio> #include <queue> using ...

  4. 【BZOJ2324】[ZJOI2011]营救皮卡丘(网络流,费用流)

    [BZOJ2324][ZJOI2011]营救皮卡丘(网络流,费用流) 题面 BZOJ 洛谷 题解 如果考虑每个人走的路径,就会很麻烦. 转过来考虑每个人破坏的点集,这样子每个人可以得到一个上升的序列. ...

  5. HDU 3416 Marriage Match IV (最短路径,网络流,最大流)

    HDU 3416 Marriage Match IV (最短路径,网络流,最大流) Description Do not sincere non-interference. Like that sho ...

  6. HDU 3605 Escape (网络流,最大流,位运算压缩)

    HDU 3605 Escape (网络流,最大流,位运算压缩) Description 2012 If this is the end of the world how to do? I do not ...

  7. HDU 3338 Kakuro Extension (网络流,最大流)

    HDU 3338 Kakuro Extension (网络流,最大流) Description If you solved problem like this, forget it.Because y ...

  8. POJ 2711 Leapin' Lizards / HDU 2732 Leapin' Lizards / BZOJ 1066 [SCOI2007]蜥蜴(网络流,最大流)

    POJ 2711 Leapin' Lizards / HDU 2732 Leapin' Lizards / BZOJ 1066 [SCOI2007]蜥蜴(网络流,最大流) Description Yo ...

  9. UVA 10480 Sabotage (网络流,最大流,最小割)

    UVA 10480 Sabotage (网络流,最大流,最小割) Description The regime of a small but wealthy dictatorship has been ...

随机推荐

  1. NOIP比赛中如何加速c++的输入输出

    NOIP比赛中如何加速c++的输入输出 在竞赛中,遇到大数据时,往往需要更快的读取方式.由于比赛中输出一般规模较小,本文只讨论输入如何加速. 现在我们生成1000000个随机数,构成1000*1000 ...

  2. pt-table-checksum和pt-table-sync使用

    pt-table-checksum和pt-table-sync使用 数据库版本:5.6.25 pt工具版本:2.2.14 主从关系一:不同机器同一端口 10.10.228.163:4306(rescs ...

  3. 怎样实现跨域AJAX请求发送Cookie

    第一步: 服务器必须在Response Header中设置: Access-Control-Allow-Credentials: true 第二步: 客户端发起请求时需要将 xhr.withCrede ...

  4. 4-MySQL DBA笔记-开发进阶

    第4章 开发进阶 本章将介绍一些重中之重的数据库开发知识.在数据库表设计中,范式设计是非常重要的基础理论,因此本章把它放在最前面进行讲解,而这其中又会涉及另一个重要的概念——反范式设计.接下来会讲述M ...

  5. 安川机器人Yaskawa

    安川机器人自动回原点 1. 判断机器人是否在安全位置  (立方体干涉区) 2. 读取机器人当前位置  GETS PX000 $PX001 读取基座标下的机器人当前位置并放到位置型变量P000中  $P ...

  6. docker启动mysql 自定义配置文件

    命令行如下: docker run --name mysql56 -p : -v /home/mysql56/data:/var/lib/mysql -v /home/mysql56/conf:/et ...

  7. VBA精彩代码分享-1

    今天下班前分享一下之前在网上搜到的两段好用的VBA代码,貌似都来自国外,觉得挺好,模仿不来. 第一段的功能是修改VBA控件中的文本框控件,使其右键可以选择粘贴.复制.剪切等: Option Expli ...

  8. SAP分析云及协同计划

    大家好, 我是SAP成都研究院S/4HANA Sales 团队的软件工程师Derek.四年前我从SAP Consulting团队转到SAP Labs从事Sales Analytics相关应用的开发,在 ...

  9. jQuery EasyUI简介

    jQuery EasyUI 是一个基于 jQuery 的框架,集成了各种用户界面插件. jQuery EasyUI是一组基于jQuery的UI插件集合体,而jQuery EasyUI的目标就是帮助we ...

  10. 英语是学习Java编程的基础吗

    就当前市场行情需求来看,Java人才需求依旧火爆,在如今互联网时代,手机移动端的软件开发是非常重要的,如今无论是大中小企业都是需要进行软件的开发的,又因为Java是开源的使用起来可以节约一大批的成本, ...