最大流 Ford-Fulkerson算法

        最大流和割的关系:

        定理一:如果f是网络中的一个流,CUT(S,T)是任意一个割,那么f的值等于正向割边的流量与负向割边的流量之差。

        证明:
        设X和Y是网络中的两个顶点集合,用f(X,Y)表示从X中的一个顶点指向Y的一个顶点的所有弧(弧尾在X中,弧头在Y中:XY)的流量和。
        只需证明:f=f(S,T)-f(T,S) 即可。下列结论成立:
        如果X∩Y=   ,那么:
                f(X,(Y1∪Y2))=f(X,Y1)+f(X,Y2)
                f((X1∪X2),Y)=f(X1,Y)+f(X2,Y)  成立。
        根据网络流的特点:

        如果V既不是源点也不是汇点,那么:
                f({V},S∪T)-f(S∪T,{V})=0,可知任何一个点,流入的与流出的量相等。
        如果V是源,那么:
               f({V},S∪T)-f(S∪T,{V})=f

        对于S中的所有点V都有上述关系式,相加得到:
               f(S,S∪T)-f(S∪T,S)=f

        又因为:
               f(S,S∪T)-f (S∪T,S)
               = (f(S,S)+f (S,T))-(f(S,S) +f (T,S))
               = f(S,T)- f(T,S)

        所以:f= f(S,T)- f(T,S)  定理得证。

 

        f= f(S,T)- f(T,S)<=f(S,T)<=割CUT(S,T)的容量。
        推论1:如果f是网络中的一个流,CUT(S,T)是一个割,那么f的值不超过割CUT(S,T)的容量。
        推论2: 网络中的最大流不超过任何割的容量 


        定理2:在任何网络中,如果f是一个流,CUT(S,T)是一个割,且f的值等于割CUT(S,T)的容量,那么f是一个最大流,CUT(S,T)是一个最小割(容量最小的割)。
        证明:

        令割CUT(S,T)的容量为C,所以流f的流量也为C。
        假设另外的任意流f1,流量为c1,根据流量不超过割的容量,则c1<=c,所以f是最大流。
        假设另外的任意割CUT(S1,T1),容量为c1,根据流量不超过割的容量,所以有c1>=c,故,CUT(S,T)是最小割。

 

        定理3:最大流最小割定量:在任何的网络中,最大流的值等于最小割的容量


        结论1:最大流时,最小割cut(S,T)中,正向割边的流量=容量,逆向割边的流量为0。否则还可以增广。
        结论2:在最小割中cut(S,T)中

        ① 源点s∈S。
        ② 如果i∈S,结点j满足:有弧<i,j>,并且c[I,j]>f[I,j] 或者有弧<j,i>并且f[j,i]>0,那么j∈S。//否则不是最小割
        即从s出发能找到的含有残留的点组成集合S。其余的点组成集合T。

        Ford-Fulkerson算法的实现:

#include <stdio.h>
#include <vector>
using namespace std;

#define MAX_N 1001
#define INF 0xffffff
#define min(a, b) (a < b ? a : b)

struct edge {
	int to, cap;
	int rev;

	edge(int t, int c, int r) {
		to = t;
		cap = c;
		rev = r;
	}
};

vector<edge> G[MAX_N];
bool visited[MAX_N];
int n, m;

// 每对顶点添加正反边
void addEdge(int from, int to, int cap) {
	G[from].push_back(edge(to, cap, G[to].size()));
	G[to].push_back(edge(from, 0, G[from].size() - 1));
}

int dfs(int v, int t, int f) {
	int i;

	if (v == t) {
		return f;
	}

	visited[v] = true;
	for (i = 0; i < G[v].size(); i++) {
		edge &e = G[v][i]; 
		if (!visited[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;
}

int maxFlow(int s, int t) {
	int flow = 0;

	while (true) {
		memset(visited, 0, sizeof(visited));
		int f = dfs(s, t, INF);

		if (f == 0) {
			return flow;
		}
		flow += f;
	}
}

int main() {
	int i, j;
	int from, to, cap;

	while (scanf("%d%d", &n, &m) != EOF && n || m) {
		for (i = 0; i < n; i++) {
			G[i].clear();
		}

		for (i = 0; i < m; i++) {
			scanf("%d%d%d", &from, &to, &cap);
			addEdge(from, to, cap);
		}

		printf("%d\n", maxFlow(0, n - 1));
	}

	return 0;
}
5 7
0 1 10
0 2 2
1 2 6
1 3 6
2 4 5
3 2 3
3 4 8
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值