hihocoder 1378 : 网络流二·最大流最小割定理

来源:互联网 发布:linux查看进程被占用 编辑:程序博客网 时间:2024/05/19 23:15

描述

小Hi:在上一周的Hiho一下中我们初步讲解了网络流的概念以及常规解法,小Ho你还记得内容么?

小Ho:我记得!网络流就是给定了一张图G=(V,E),以及源点s和汇点t。每一条边e(u,v)具有容量c(u,v)。网络流的最大流问题求解的就是从s到t最多能有多少流量。

小Hi:那这个问题解决办法呢?

小Ho:解决网络流的基本思路就是寻找增广路,不断更新残留网络。直到找不到新的增广路,此时得到的流就是该网络的最大流。

小Hi:没错,看来你记得很牢嘛。

小Ho:哎嘿嘿,不过这里我有一个问题,为什么找不到增广路时就已经找到了最大流呢?

小Hi:这一次我就来解决你的疑惑,首先我们要从网络流的割开始讲起。

对于一个网络流图G=(V,E),其割的定义为一种点的划分方式:将所有的点划分为S和T=V-S两个部分,其中源点s∈S,汇点t∈T。

对于一个割(S,T),我们定义净流f(S,T)表示穿过割(S,T)的流量之和,即:

f(S,T) = Σf(u,v) | u∈S,v∈T

举个例子(该例子选自算法导论):

净流f = f(2,4)+f(3,4)+f(3,5) = 12+(-4)+11 = 19

同时我们定义割的容量C(S,T)为所有从S到T的边容量之和,即:

C(S,T) = Σc(u,v) | u∈S,v∈T

同样在上面的例子中,其割的容量为:

c(2,4)+c(3,5)=12+14=26

小Ho:也就是说在计算割(S,T)的净流f(S,T)时可能存在反向的流使得f(u,v)<0,而容量C(S,T)一定是非负数。

小Hi:你这么说也没错。实际上对于任意一个割的净流f(S,T)总是和网络流的流量f相等。比如上面例子中我们改变一下割的方式:

可以计算出对于这两种情况净流f(S,T)仍然等于19。

一个直观的解释是:根据网络流的定义,只有源点s会产生流量,汇点t会接收流量。因此任意非s和t的点u,其净流量一定为0,也即是Σ(f(u,v))=0。而源点s的流量最终都会通过割(S,T)的边到达汇点t,所以网络流的流f等于割的静流f(S,T)。

严格的证明如下:

f(S,T) = f(S,V) - f(S,S)从S到T的流等于从S到所有节点的流减去从S到S内部节点的流f(S,T) = f(S,V)由于S内部的节点之间存在的流一定有对应的反向流,因此f(S,S)=0f(S,T) = f(s,V) + f(S-s,V)再将S集合分成源点s和其他属于S的节点f(S,T) = f(s,V)由于除了源点s以外其他节点不会产生流,因此f(S-s,V)=0f(S,T) = f(s,V) = f

所以f(S,T)等于从源点s出来的流,也就是网络的流f。

小Ho:简单理解的话,也就是说任意一个割的净流f(S,T)都等于当前网络的流量f

小Hi:是这样的。而对于任意一个割的净流f(S,T)一定是小于等于割的容量C(S,T)。那也即是,对于网络的任意一个流f一定是小于等于任意一个割的容量C(S,T)。

而在所有可能的割中,存在一个容量最小的割,我们称其为最小割

这个最小割限制了一个网络的流f上界,所以有:

对于任一个网络流图来说,其最大流一定是小于等于最小割的。

小Ho:但是这和增广路又有什么关系呢?

小Hi:接下来就是重点了。利用上面讲的知识,我们可以推出一个最大流最小割定理

对于一个网络流图G=(V,E),其中有源点s和汇点t,那么下面三个条件是等价的:1. 流f是图G的最大流2. 残留网络Gf不存在增广路3. 对于G的某一个割(S,T),此时f = C(S,T)

首先证明1 => 2

我们利用反证法,假设流f是图G的最大流,但是残留网络中还存在有增广路p,其流量为fp。则我们有流f'=f+fp>f。这与f是最大流产生矛盾。

接着证明2 => 3

假设残留网络Gf不存在增广路,所以在残留网络Gf中不存在路径从s到达t。我们定义S集合为:当前残留网络中s能够到达的点。同时定义T=V-S。此时(S,T)构成一个割(S,T)。且对于任意的u∈S,v∈T,有f(u,v)=c(u,v)。若f(u,v)<c(u,v),则有Gf(u,v)>0,s可以到达v,与v属于T矛盾。因此有f(S,T)=Σf(u,v)=Σc(u,v)=C(S,T)。

最后证明3 => 1

由于f的上界为最小割,当f到达割的容量时,显然就已经到达最大值,因此f为最大流。

这样就说明了为什么找不到增广路时,所求得的一定是最大流。

小Ho:原来是这样,我明白了。

输入

第1行:2个正整数N,M。2≤N≤500,1≤M≤20,000。

第2..M+1行:每行3个整数u,v,c(u,v),表示一条边(u,v)及其容量c(u,v)。1≤u,v≤N,0≤c(u,v)≤100。

给定的图中默认源点为1,汇点为N。可能有重复的边。

输出

第1行:2个整数A B,A表示最小割的容量,B表示给定图G最小割S集合的点数。

第2行:B个空格隔开的整数,表示S集合的点编号。

若存在多个最小割可以输出任意一个的解。

样例输入
6 71 2 31 3 52 4 13 4 23 5 34 6 45 6 2
样例输出
5 41 2 3 5

思路:要求最大流和最小割的集合,最大流可以使用查找增广路的办法查找,最后的最小割可以在最后查找一次增光路记最小割的集合,具体看代码:

#include <iostream>#include <cstdio>#include <cstring>#include <cmath>#include <algorithm>#include <climits>#include <vector>#include <queue>#include <cstdlib>#include <string>#include <set>#include <stack>using namespace std;const int max_n  = 505;const int inf = 0xfffffff;int gra[max_n][max_n];vector<int>v[max_n];int level[max_n];int n,m;vector<int>mp;int flag;bool bfs() {    queue<int>q;    memset(level,0,sizeof(level));    q.push(1);    level[1] = 1;    if(flag) {        mp.push_back(1);    }    while(!q.empty()) {        int p = q.front();        q.pop();        if(p == n) {            return true;        }        for(int i = 0; i < v[p].size(); i++) {            int st = v[p][i];            if(!level[st]&&gra[p][st]) {                level[st] = level[p]+1;                q.push(st);                if(flag) {                    mp.push_back(st);                }            }        }    }    return false;}int dfs(int x,int sup) {    if(x == n) {        return sup;    }    int ret = 0;    for(int i = 0; i < v[x].size(); i++) {        int st = v[x][i];        if(level[st] == level[x]+1 && gra[x][st]) {            int mi = min(sup-ret,gra[x][st]);            int mx = dfs(st,mi);            gra[x][st] -= mx;            gra[st][x] += mx;            ret += mx;            if(ret == sup) {//这里的返回是说当前的点所能流过的最大流,和后面所有能流过的总和相等是,便不可能再允许后面的流量流过                return ret;            }        }    }    return ret;}int max_flow() {    int ans = 0;    while(bfs()) {        ans += dfs(1,inf);    }    return ans;}int main() {    scanf("%d%d",&n,&m);    memset(gra,0,sizeof(gra));    int a,b,c;    for(int i = 0; i < m; i++) {        scanf("%d%d%d",&a,&b,&c);        v[a].push_back(b);        gra[a][b] += c;    }    int ans = max_flow();    printf("%d ",ans);    mp.clear();    flag = 1;    bfs();    printf("%d\n",mp.size());    printf("%d",mp[0]);    for(int i = 1;i < mp.size();++i){        printf(" %d",mp[i]);    }    printf("\n");    return 0;}
总结:自己对最大流的理解还是很欠缺。。。。。。继续加油


0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 苹果手机忘记了锁屏密码怎么办 小米5的4g网速慢怎么办 红米4a卡机了怎么办 信而富逾期3个月怎么办 苹果6锁屏密码忘了怎么办 孕8个月咳嗽有痰怎么办 微信红包输了6万怎么办 红米3s开不了机怎么办 核载5人载了6人怎么办 我欠支付宝2万6怎么办 我47岁这个月经不来怎么办 两个月不来月经了也没怀孕怎么办 婴儿不吃奶粉母乳又不够吃怎么办 怀孕39周了还没反应怎么办 脸过敏了又红又痒怎么办 刚开的淘宝店没生意怎么办 我22岁长得显老怎么办 卡的钱被qq转走怎么办 招行u盾密码忘了怎么办 孩子上五年级了成绩非常差怎么办 红米3s开不开机怎么办 皮肤被虫子咬了红肿痒怎么办 微信被骗了1万多怎么办 6个月宝宝吃了纸怎么办 农行k宝扣了50块怎么办 4g流量用的太快怎么办 怀疑老公有外遇最明智的怎么办 咽喉疼怎么办最简单的方法如下 生完孩子后腰疼的厉害怎么办 眼睛进东西了弄不出来怎么办 18k金不给换黄金怎么办 我22岁欠了10万怎么办 1岁宝宝又吐又拉怎么办 月经10天了还没干净怎么办 舌头有异味怎么办是有口臭吗 快8个月羊水破了怎么办 25岁欠了50万债怎么办 28岁血压高150低压110怎么办 苹果6的4g网络慢怎么办 一个月染了6次头怎么办 五0二干在衣服上怎么办