基于Ford-Fulkerson算法的最大流,最小割问题

问题描述:

最大传输量: 

网络中有两台计算机s(source)和t(sink),现在想从s传输数据到t,该网络中一共有N台计算机,其中一些计算机之间有一条单向的通信电缆,每条通信电缆都有对应的1s内所能传输的最大的数据量。当其他计算机之间没有数据传输时,在1秒内s最多可以传送多少数据到t?    

Ford-Fulkerson算法

 把计算机当作顶点,把连接计算机的通信电缆当作边,就可以当作有向图G(V,E)来考虑。               图中的每条边e\epsilon E,都有对应的最大可能的数据传输量c(e)。这样,就可以把问题转为如下形式。

  • 记每条边对应的实际数据传输量为f(e)
  • 传输量满足

 0\leq f(e)\leq c(e)

  •  数据在传输过程中既不会减少也不会增加,收到的数据量和发出的数据量应该相等

 \forall v\epsilon V\setminus \left \{ s\ t \right \}\sum_{e\epsilon \delta_{-} \left ( v \right )}^{}f(e)=\sum_{e\epsilon \delta_{+}\left ( v \right )}^{}f(e)

  •  目标是最大化从s出发的数据量

\sum_{e\epsilon \delta_{+}\left ( s \right )}^{}f(e)   

 首先考虑贪心算法

  1.  寻找w

    w:s\rightarrow t\ \forall e\epsilon w\ f(e)<c(e) 

    2.   如果不存在满足条件的路径,则结束。否则,沿该路径尽可能的增加f(e),如此递归。

很容易发现这样得到的结果并非最大值。(不满足贪心选择性)。

贪心法的两条路径:w1:s->1->2->t;w2:s->1->3->t

 f(w1)+f(w2)=10

 而实际的最大值为11:

\left\{\begin{matrix}w1:s\to2\to t\ \ \ \ \ \ \\ w2:s\to1\to2\to t \\ w3:s\to1\to3\to t \end{matrix}\right.           \left\{\begin{matrix}f(w1)=1 \\ f(w2)=4 \\ f(w3)=6 \end{matrix}\right.

\sum_{w}^{}f(e)=11

贪心选择性的矛盾:对于一条路w,可能会影响之后路的流,于是在贪心之后可以对w撤销f'(e),以供w'选择。于是可将算法进行如下改进。

  1.    只利用f(e)<c(e)的边或者满足f(e)>0的边对应的反向边rev(e),寻找w:s\rightarrow t
  2.    如果不满足这样的路径,则结束。否则,尽可能的增加流,并如此递归

称1中对应的边和E组成的图叫残余网络

称残余网络中的s\rightarrow t路径为增广路

//Ford-Fulkerson算法领接表实现。
//没有保存f(e)的值,而是直接改变c(e)的值

#include<iostream>
#include<algorithm>
#include<vector>
#include<cmath>
using namespace std;

#define MAX_V 1<<10
#define INF 0x3f3f3f3f
#define int long long

//用于边的结构体
typedef struct edge { int to, cap, rev; }edge;

//图的领接表
vector<edge>G[MAX_V];
//DFS访问标记
bool used[MAX_V];

//向图中增加一条从s到t容量为cap的边
void add_edge(int from, int to, int cap) {
    edge  a = { to,cap,G[to].size() };
    edge  b = { from,0,G[from].size()};
    G[from].push_back(a);
    G[to].push_back(b);
}

//通过DFS找路
int dfs(int v, int t, int f) {
    if (v == t) return f;
    used[v] = true;
    for (int i = 0; i < G[v].size(); i++) {
        edge& e = G[v][i];
        if (!used[e.to] && e.cap > 0) {
            int d = dfs(e.to, t, min(f, e.cap));
            if (d > 0) {
                e.cap -= d;
                G[e.to][e.rev].cap += d;
                return d;
            }
        }
    }
    return 0;
}


//求解从s到t的最大流
int max_flow(int s, int t) {
    int flow = 0;
    for (;;) {
        memset(used, 0, sizeof(used));
        int f = dfs(s, t, INF);
        if (f == 0) return flow;
        flow += f;
    }
}

unsigned main() {
    ios::sync_with_stdio(false);
    //to do code
}

记最大流为F,Ford-Fulkerson算法最多进行F次深度优先搜索,所以其复杂度为O(F|E|)

不过这是一个很松的上界,达到这样最坏复杂度的情况几乎不存在。

那么这个算法总能求出最大流吗?答案是肯定的,下面根据最小割来说明。

考虑如下问题:对于给定网络,为了保证没有w:s\rightarrow t,需要删去的边的总容量的最小值是多少。

最小割

考虑

\forall f:s \to t\ \forall cut(S,v\setminus S):s \to t

\because \sum_{w\epsilon W}f(w)=\sum_{e\epsilon \delta_{+}\left ( s\right )}^{}f(e)

\because \forall v\epsilon S\setminus s\sum_{e\epsilon \delta_{-} \left ( v \right )}^{}f(e)=\sum_{e\epsilon \delta_{+}\left ( v \right )}^{}f(e)

\therefore\sum_{w\epsilon W}f(w)=\sum_{e\epsilon \delta_{+}\left ( S\right )}^{}f(e)-\sum_{e\epsilon \delta_{-}\left ( S\right )}^{}f(e)

由此可见

\sum f(e)\leq \sum_{e\epsilon cut:S\rightarrow V-S}cap(e)

考虑通过Ford-Fulkerson算法求得的流f'.记得f'对应的残余网络中S=\left \{ e|\exists w:s \to e \right \},因为f'对应的残局网络中

w:s \to t=\varnothing\therefore cut\left ( S,V\setminus S \right ):s \to t

根据S的定义

\left\{\begin{matrix}\forall e\ \epsilon\ cut\ f'(e)=c(e) \\\forall e\notin cut\ f'(e)=0 \end{matrix}\right.

 \therefore\sum_{w\epsilon W}f'(w)=\sum_{e\epsilon \delta_{+}\left ( S\right )}^{}f(e)-\sum_{e\epsilon \delta_{-}\left ( S\right )}^{}f(e)=\sum_{e\epsilon cut:S\rightarrow V-S}cap(e)

再由不等式知

MAX\left ( \sum_{e\epsilon \delta_{+}\left ( s \right )}^{}f(e) \right )=\sum_{w\epsilon W}f'(w)

 由此证明了Ford-Fulkerson算法的正确性,同时得到:

 最大流等于最小割

本文借鉴: [日]秋叶拓哉:挑战程序设计

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值