hihocoder1378 网络流之最大流最小割

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_24477135/article/details/53025701

题目链接:http://hihocoder.com/problemset/problem/1378

思路:


描述

小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)=0
f(S,T) = f(s,V) + f(S-s,V)
再将S集合分成源点s和其他属于S的节点
f(S,T) = f(s,V)
由于除了源点s以外其他节点不会产生流,因此f(S-s,V)=0
f(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为最大流。

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



#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <cmath>
#include <stack>
#include <queue>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <stdlib.h>
#include <iomanip>
#include <fstream>

using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define maxn 505
#define MOD 1000000007
#define mod 2147493647
#define mem(a , b) memset(a , b , sizeof(a))
#define LL long long
#define ULL unsigned long long
#define FOR(i , n) for(int i = 1 ;  i<= n ; i ++)
typedef pair<int , int> pii;
int n , m , t;
vector<int>V[maxn];
int a[maxn][maxn] , path[maxn];
bool vis[maxn] ;
int flow[maxn];

bool GetAugmentPath()
{
    queue<int>q;
    while(!q.empty()) q.pop();
    q.push(1);vis[1] = 1;
    flow[1] = MOD;
    while(!q.empty())
    {
        int cur = q.front();
        q.pop();
        if(cur == n)
        {
            return 1;
        }
        for(int i = 0 ; i < V[cur].size() ; i ++)
        {
            if(!vis[V[cur][i]] && a[cur][V[cur][i]] > 0)
            {
                vis[V[cur][i]] = 1;
                path[V[cur][i]] = cur;
                flow[V[cur][i]] = min(a[cur][V[cur][i]] , flow[cur]);
                q.push(V[cur][i]);

            }
        }
    }
    return 0;
}

void Update(int num)
{
    int u = n , v = path[u];
    while(v != -1)
    {
        a[v][u] -= num;
        a[u][v] += num;
        u = v;
        v = path[u];
    }
    return ;
}

int main()
{
    //scanf("%d" , &t);
    int ncase = 1;
    while(scanf("%d %d" , &n , &m) != EOF)
    {

        for(int i = 0 ; i <= n ; i ++) V[i].clear();
        mem(a , 0);mem(vis , 0);mem(path , -1);mem(flow , 0);
        int u , v , c ;
        for(int i = 0 ; i < m ; i ++)
        {
            scanf("%d %d %d" , &u , &v , &c);
            a[u][v] += c ;
          //  a[v][u] = max(0 , a[v][u]);
            V[u].push_back(v);
            V[v].push_back(u);
        }
        int ans = 0;
        while(GetAugmentPath())
        {
            ans += flow[n];
            Update(flow[n]);
            mem(vis , 0);mem(path , -1);mem(flow , 0);
        }
        int Node[maxn] , id = 0;
        for(int i = 1 ; i <= n ; i ++)
        {
            if(vis[i])
            {
                Node[id++] = i;
            }
        }
        printf("%d %d\n" , ans , id);
        for(int i = 0 ; i < id; i ++)
        {
            printf("%d" , Node[i]);
            if(i != id - 1) printf(" ");
          //  else printf("%d\n");
        }
        cout << endl;
    }
    return 0;
}


猜你喜欢

转载自blog.csdn.net/qq_24477135/article/details/53025701