网络流最大流-Ford-Fullerson和dinic算法,最小割

网络就是一张带权有向图,把其看作一个输水的管道更直观一点,源点就是整个系统的水源来处,汇点就是水流最后到达的地方。其他的中间点都是闸门,闸门是不会产生和私吞水量的,而每个闸门之间都是由管道连接的,这个管道决定了你能在这个通道中间最大传多少水。每个闸门之间又会交错相连,由此也就有了最大流问题,也就是这个网络最大的流量是多少。
我们还需要知道一个定义:
增广路:从源点找到一条能达到汇点的路,这条路会使总流量增加,就叫做增广路。
找最大流也就是不断的找增广路的过程,直到所有的增广路都被找过。
这个时候可能大家脑海里已经想到了深搜,但深搜的问题还得通过别的方法解决一下,深搜有什么问题呢?
比如
在这里插入图片描述
这张图通过深搜肯定会找到v1->v2->v5->v6;
但实际这条路我们并不需要,这个时候就需要给程序一个后悔的机会,能让他把流过去的流量在流回来。因此就有了这个神奇的操作,加一条反向边,如果这条边走过流量f,就把这条边的流量减去f,反向边的流量就加f。

代码如下:

 #include <iostream>
#include <cstdio>
#include <cstring> 
#include <vector> 
#include <algorithm>

using namespace std;

const int MAX_V = 500001;
const int INF = 0x3f3f3f3f;

struct edge
{
	//终点,容量,反向边 
	int to, cap, rec;
};

struct Node
{
	int from, to;
};

vector<Node> p;
vector<edge> G[MAX_V];
bool used[MAX_V];

//增加一条从s到t容量为cap的边
void add_edge(int from, int to, int cap)
{
	//正向边,反向边应是第i+1条边,但数组是从0开始的,因此直接加to的size即可
	G[from].push_back((edge){to, cap, G[to].size()});
	//本来应该是第i条边是反向边,但是上面push后size变为了i+1,因此需要减去1
	G[to].push_back((edge){from, 0, G[from].size()-1});
} 

//用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.rec].cap+=d;
				return d;
			}
		}
	}
	
	return 0;
}

int max_flow(int s, int t)
{
	int flow = 0;
	
	while (1)
	{
		memset(used, 0, sizeof (used));
		
		int f = dfs(s, t, INF);
		
		if (f == 0)
		{
			return flow;
		}
		
		flow+=f;
	}
}

int main()
{
	int n, m;
	
	cin >> n >> m;
	
	for (int i=0; i<m; i++)
	{
		int a, b, cost;
		
		cin >> a >> b >> cost;
		
		add_edge(a, b, cost);
	}
	
	cout << max_flow(1, n) << endl;
	
	return 0;
} 

我们一般打题都用Dicnic,因为它要比FF快好多
明白了For-Fullerson算法后,Dicnic算法就简单多了,Dicnic算法就是进行一步预处理,通过BFS将图先进行分层操作,然后再用DFS查找增广路。分层后,进行DFS时,只找level高的节点进行深搜,减少了许多不必要的分支。然后其他就都差不多了。
代码如下:

#include <iostream>
#include <vector>
#include <queue>
#include <cstring>

using namespace std;

const int MAX_N = 500001;
const int INF = 0x3f3f3f3f;

struct edge
{
	int to, cap, rev;
};

vector<edge> G[MAX_N];
int level[MAX_N];

void add_edge(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});
}

void bfs(int s)
{
	memset(level, -1, sizeof(level));
	
	queue<int> que;
	level[s] = 0;
	que.push(s);
	
	while (!que.empty())
	{
		int v = que.front();
		
		que.pop();
		
		for (int i=0; i<G[v].size(); i++)
		{
			edge &e = G[v][i];
			
			if (e.cap > 0 && level[e.to] < 0)
			{
				level[e.to] = level[v] + 1;
				que.push(e.to);
			}
		}
	}
}

int dfs(int v, int t, int f)
{
	if (v == t)
	{
		return f;
	}
	
	for (int i = 0; i < G[v].size(); i++)
	{
		edge &e = G[v][i];
		
		if (e.cap > 0 && level[v] < level[e.to])
		{
			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 max_flow(int s, int t)
{
	int flow = 0;
	
	for (;;)
	{
		bfs(s);
		
		if (level[t] < 0)
		{
			return flow;
		}
		
		int f;
		
		while ((f = dfs(s, t, INF)) > 0)
		{
			flow+=f;
		}
	}
}

int main()
{
	int n, m;
	
	cin >> n >> m;
	
	for (int i=0; i<m; i++)
	{
		int a, b, cost;
		
		cin >> a >> b >> cost;
		
		add_edge(a, b, cost);
	}
	
	cout << max_flow(1, n) << endl;
	
	return 0;
}

然后就是最小割了,为什么在最大流这里面写最小割呢?
二者其实就是一个东西,什么是最小割,就是切断一些边把两个点变得没有一点联系(切断的代价与权值相等),无法通过其中一个点到达另一个点。有一个定理就是最大流等于最小割。所以求最小割的话,就跑一遍最大流就行。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值