热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

(教你彻底理解)网络流:基本概念与算法最大流最小割

原文链接:http:www.cnblogs.comBooblearchive201103041970453.html一.网络流:流&网络&割1.网络流问题(NetWo

原文链接:http://www.cnblogs.com/Booble/archive/2011/03/04/1970453.html

一.网络流:流&网络&

1.网络流问题(NetWork Flow Problem):

给定指定的一个有向图,其中有两个特殊的点源S(Sources)和汇T(Sinks),每条边有指定的容量(Capacity),求满足条件的从S到T的最大流(MaxFlow).

The network flow problem considers a graph G with a set of sources S and sinks T and for which each edge has an assigned capacity (weight), and then asks to find the maximum flow that can be routed from S to T while respecting the given edge capacities.

http://mathworld.wolfram.com/NetworkFlow.html

下面给出一个通俗点的解释

(下文基本避开形式化的证明 基本都用此类描述叙述)

好比你家是汇 自来水厂(有需要的同学可以把自来水厂当成银行之类 以下类似)是源

然后自来水厂和你家之间修了很多条水管子接在一起 水管子规格不一 有的容量大 有的容量小

然后问自来水厂开闸放水 你家收到水的最大流量是多少

如果自来水厂停水了 你家那的流量就是0 当然不是最大的流量

但是你给自来水厂交了100w美金 自来水厂拼命水管里通水 但是你家的流量也就那么多不变了 这时就达到了最大流

-------------------------------------------------------------------------------------------------------------

2.三个基本的性质:

如果 C代表每条边的容量 F代表每条边的流量

一个显然的实事是F小于等于C 不然水管子就爆了

这就是网络流的第一条性质 容量限制(Capacity Constraints):F ≤ C

再考虑节点任意一个节点 流入量总是等于流出的量 否则就会蓄水(爆炸危险...)或者平白无故多出水(有地下水涌出?)

这是第二条性质 流量守恒(Flow Conservation):Σ F = Σ F

当然源和汇不用满足流量守恒 我们不用去关心自来水厂的水是河里的 还是江里的

(插播广告: 节约水资源 人人有责!)

最后一个不是很显然的性质 是斜对称性(Skew Symmetry): F = - F

这其实是完善的网络流理论不可缺少的 就好比中学物理里用正负数来定义一维的位移一样

百米起点到百米终点的位移是100m的话 那么终点到起点的位移就是-100m

同样的 x向y流了F的流 y就向x流了-F的流

-------------------------------------------------------------------------------------------------------------

3.容量网络&流量网络&残留网络:

网络就是有源汇的有向图 关于什么就是指边权的含义是什么

容量网络就是关于容量的网络 基本是不改变的(极少数问题需要变动)

流量网络就是关于流量的网络 在求解问题的过程中

通常在不断的改变 但是总是满足上述三个性质

调整到最后就是最大流网络 同时也可以得到最大流值

残留网络往往概括了容量网络和流量网络 是最为常用的

残留网络=容量网络-流量网络

这个等式是始终成立的 残留值当流量值为负时甚至会大于容量值

流量值为什么会为负?有正必有负,记住斜对称性!

-------------------------------------------------------------------------------------------------------------

4.割&割集:

无向图的割集(Cut Set):C[A,B]是将图G分为A和B两个点集 A和B之间的边的全集

A set of edges of a graph which, if removed (or "cut"), disconnects the graph (i.e., forms a disconnected graph).

http://mathworld.wolfram.com/CutSet.html

网络的割集:C[S,T]是将网络G分为s和t两部分点集 S属于s且T属于t 从S到T的边的全集

带权图的割(Cut)就是割集中边或者有向边的权和

Given a weighted, undirected graph G=(V,E) and a graphical partition of V into two sets A and B, the cut of G with respect to A and B is defined as cut(A,B)=sum_(i in A,j in B)W(i,j),where W(i,j) denotes the weight for the edge connecting vertices i and j. The weight of the cut is the sum of weights of edges crossing the cut.

http://mathworld.wolfram.com/Cut.html

通俗的理解一下:

割集好比是一个恐怖分子 把你家和自来水厂之间的水管网络砍断了一些

然后自来水厂无论怎么放水 水都只能从水管断口哗哗流走了 你家就停水了

(插播广告: 节约水资源 人人有责!)

割的大小应该是恐怖分子应该关心的事 毕竟细管子好割一些

而最小割花的力气最小

==================================================================================

二.计算最大流的基本算法

那么怎么求出一个网络的最大流呢?

这里介绍一个最简单的算法:Edmonds-Karp算法 即最短路径增广算法 简称EK算法

EK算法基于一个基本的方法:Ford-Fulkerson方法 即增广路方法 简称FF方法

增广路方法是很多网络流算法的基础 一般都在残留网络中实现

其思路是每次找出一条从源到汇的能够增加流的路径 调整流值和残留网络 不断调整直到没有增广路为止

FF方法的基础是增广路定理(Augmenting Path Theorem):网络达到最大流当且仅当残留网络中没有增广路

证明略 这个定理应该能够接受的吧

EK算法就是不断的找最短路 找的方法就是每次找一条边数最少的增广 也就是最短路径增广

这样就产生了三个问题:

-------------------------------------------------------------------------------------------------------------

1.最多要增广多少次?

可以证明 最多O(VE)次增广 可以达到最大流 证明略

2.如何找到一条增广路?

先明确什么是增广路 增广路是这样一条从s到t的路径 路径上每条边残留容量都为正

把残留容量为正的边设为可行的边 那么我们就可以用简单的BFS得到边数最少的增广路

3.如何增广?

BFS得到增广路之后 这条增广路能够增广的流值 是路径上最小残留容量边决定的

把这个最小残留容量MinCap值加到最大流值Flow上 同时路径上每条边的残留容量值减去MinCap

最后 路径上每条边的反向边残留容量值要加上MinCap 为什么? 下面会具体解释

-------------------------------------------------------------------------------------------------------------

这样每次增广的复杂度为O(E) EK算法的总复杂度就是O(VE^2)

事实上 大多数网络的增广次数很少 EK算法能处理绝大多数问题

平均意义下增广路算法都是很快的

增广路算法好比是自来水公司不断的往水管网里一条一条的通水

上面还遗留了一个反向边的问题: 为什么增广路径上每条边的反向边残留容量值要加上MinCap?

因为斜对称性! 由于残留网络=容量网络-流量网络

容量网络不改变的情况下

由于增广好比给增广路上通了一条流 路径说所有边流量加MinCap

流量网络中路径上边的流量加MinCap 反向边流量减去MinCap

相对应的残留网络就发生相反的改变

这样我们就完成了EK算法 具体实现可以用邻接表存图 也可以用邻接矩阵存图

邻接表存图 由于流量同时存在于边与反向边 为了方便求取反向边 建图把一对互为反向边的边建在一起

代码很简单 最好自己实现一下

EK

看一个具体的增广路算法的例子吧

==================================================================================

三.最大流最小割定理

下面介绍网络流理论中一个最为重要的定理

最大流最小割定理(Maximum Flow, Minimum Cut Theorem):网络的最大流等于最小割

The maximum flow between vertices v_i and v_j in a graph G is exactly the weight of the smallest set of edges to disconnect G with v_i and v_j in different components.

http://mathworld.wolfram.com/MaximumFlowMinimumCutTheorem.html

具体的证明分三部分

1.任意一个流都小于等于任意一个割

这个很好理解 自来水公司随便给你家通点水 构成一个流

恐怖分子随便砍几刀 砍出一个割

由于容量限制 每一根的被砍的水管子流出的水流量都小于管子的容量

每一根被砍的水管的水本来都要到你家的 现在流到外面 加起来得到的流量还是等于原来的流

管子的容量加起来就是割 所以流小于等于割

由于上面的流和割都是任意构造的 所以任意一个流小于任意一个割

2.构造出一个流等于一个割

当达到最大流时 根据增广路定理

残留网络中s到t已经没有通路了 否则还能继续增广

我们把s能到的的点集设为S 不能到的点集为T

构造出一个割集C[S,T] S到T的边必然满流 否则就能继续增广

这些满流边的流量和就是当前的流即最大流

把这些满流边作为割 就构造出了一个和最大流相等的割

3.最大流等于最小割

设相等的流和割分别为Fm和Cm

则因为任意一个流小于等于任意一个割

任意F≤Fm=Cm≤任意C

定理说明完成

==================================================================================

四.简单的应用

Poj 1459是一个很典型的网络流应用

把电流想象成水流

http://poj.org/problem?id=1459

注意把多源多汇转化为单源单汇即可利用EK算法解决问题

网络流的应用还有很多 化归的思想是网络流最具魅力的地方

代码如下:

PowerNet
const maxh=10;maxn=100; maxq=110;num:set of char=['0'..'9'];oo=1000000;var c,f:array[0..maxn+1,0..maxn+1]of longint;n,m,k1,k2,tx,hx,head,tail,s,t,x,y,z,i:longint;pre,h:array[0..maxn+1]of longint;p:array[0..maxn+1]of boolean;q:array[1..maxq]of longint;procedure getc(var x:longint);var ch:char;begin
x:=0;
read(ch);
while not(ch in num) doread(ch);
while ch in num dobeginx:=x*10+ord(ch)-48;read(ch);end;
end;
procedure pop;
begin
p[q[head]]:=false;
inc(head); inc(hx);
if head>maxq then head:=1;
end;
procedure push(x:longint);
begin
inc(tail); inc(tx);
if tail>maxq then tail:=1;
q[tail]:=x; p[x]:=true;
end;
function min(x,y:longint):longint;
begin
min:=x;
if yend;
begin
assign(input,'PowerNet.in'); reset(input);
assign(output,'PowerNet.out'); rewrite(output);
while not seekeof dobeginread(n,k1,k2,m);s:&#61;n; t:&#61;n&#43;1;fillchar(c,sizeof(c),0);for i:&#61;1 to m dobegingetc(x); getc(y); getc(z);c[x,y]:&#61;c[x,y]&#43;z;end;for i:&#61;1 to k1 dobegingetc(x); getc(y);c[s,x]:&#61;y;end;for i:&#61;1 to k2 dobegingetc(x); getc(y);c[x,t]:&#61;y;end;hx:&#61;1; tx:&#61;0;head:&#61;1; tail:&#61;0;fillchar(p,sizeof(p),false);p[s]:&#61;true; p[t]:&#61;true;fillchar(f,sizeof(f),0);fillchar(pre,sizeof(pre),0);fillchar(h,sizeof(h),0);h[s]:&#61;maxh; dec(n);for i:&#61;0 to n doif c[s,i]>0then beginh[i]:&#61;1; pre[i]:&#61;c[s,i];f[s,i]:&#61;c[s,i]; f[i,s]:&#61;-c[s,i];push(i);end;while hx<&#61;tx dobeginx:&#61;q[head];for i:&#61;0 to t dobeginy:&#61;c[x,i]-f[x,i];if (h[x]&#61;h[i]&#43;1)and(y>0)then beginif not p[i] then push(i);z:&#61;min(pre[x],y);f[x,i]:&#61;f[x,i]&#43;z;f[i,x]:&#61;f[i,x]-z;pre[x]:&#61;pre[x]-z;pre[i]:&#61;pre[i]&#43;z;end;if pre[x]&#61;0 then break;end;pop;if pre[x]>0then beginy:&#61;oo;for i:&#61;0 to t doif c[x,i]>f[x,i]then y:&#61;min(y,h[i]);h[x]:&#61;y&#43;1;push(x);end;end;writeln(pre[t]);end;
close(input); close(output);
end.





推荐阅读
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • GetWindowLong函数
    今天在看一个代码里头写了GetWindowLong(hwnd,0),我当时就有点费解,靠,上网搜索函数原型说明,死活找不到第 ... [详细]
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • 本文介绍了P1651题目的描述和要求,以及计算能搭建的塔的最大高度的方法。通过动态规划和状压技术,将问题转化为求解差值的问题,并定义了相应的状态。最终得出了计算最大高度的解法。 ... [详细]
  • HDFS2.x新特性
    一、集群间数据拷贝scp实现两个远程主机之间的文件复制scp-rhello.txtroothadoop103:useratguiguhello.txt推pushscp-rr ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 本文介绍了南邮ctf-web的writeup,包括签到题和md5 collision。在CTF比赛和渗透测试中,可以通过查看源代码、代码注释、页面隐藏元素、超链接和HTTP响应头部来寻找flag或提示信息。利用PHP弱类型,可以发现md5('QNKCDZO')='0e830400451993494058024219903391'和md5('240610708')='0e462097431906509019562988736854'。 ... [详细]
  • 本文介绍了一个题目的解法,通过二分答案来解决问题,但困难在于如何进行检查。文章提供了一种逃逸方式,通过移动最慢的宿管来锁门时跑到更居中的位置,从而使所有合格的寝室都居中。文章还提到可以分开判断两边的情况,并使用前缀和的方式来求出在任意时刻能够到达宿管即将锁门的寝室的人数。最后,文章提到可以改成O(n)的直接枚举来解决问题。 ... [详细]
  • 本文详细介绍了如何使用MySQL来显示SQL语句的执行时间,并通过MySQL Query Profiler获取CPU和内存使用量以及系统锁和表锁的时间。同时介绍了效能分析的三种方法:瓶颈分析、工作负载分析和基于比率的分析。 ... [详细]
  • Ihavethefollowingonhtml我在html上有以下内容<html><head><scriptsrc..3003_Tes ... [详细]
  • 本文讨论了一个数列求和问题,该数列按照一定规律生成。通过观察数列的规律,我们可以得出求解该问题的算法。具体算法为计算前n项i*f[i]的和,其中f[i]表示数列中有i个数字。根据参考的思路,我们可以将算法的时间复杂度控制在O(n),即计算到5e5即可满足1e9的要求。 ... [详细]
  • 电话号码的字母组合解题思路和代码示例
    本文介绍了力扣题目《电话号码的字母组合》的解题思路和代码示例。通过使用哈希表和递归求解的方法,可以将给定的电话号码转换为对应的字母组合。详细的解题思路和代码示例可以帮助读者更好地理解和实现该题目。 ... [详细]
  • 本文介绍了九度OnlineJudge中的1002题目“Grading”的解决方法。该题目要求设计一个公平的评分过程,将每个考题分配给3个独立的专家,如果他们的评分不一致,则需要请一位裁判做出最终决定。文章详细描述了评分规则,并给出了解决该问题的程序。 ... [详细]
author-avatar
心胸宽大的榛子lcf
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有