娘加载较慢请耐心等待qwq~~~看板

网络流学习笔记

网络流初步

一个网络G=(V,E)是一张有向图,图中每条有向边(x,y)E都有一个给定的权值c(x,y),称为边的容量。图中还有两个节点ST,源点和汇点。
网络的流函数:f(x,y)具有一下特性:
1、容量限制,f(x,y)c(x,y)
2、斜对称,f(x,y)=f(y,x)
3、流量守恒,xS&xT,(u,x)Ef(u,x)=(x,v)Ef(x,v)
f(x,y)为边的流量,则c(x,y)f(x,y)为边的剩余流量,对于每条边,都有一个反向边,且反向边的流量是负流量。

最大流

使得整张网络的(S,v)Ef(S,v最大的流函数被称为网络的最大流,此时流量被称为网络流的最大流量。

利用最大流求二分图匹配数量

可以新增一个S节点和一个T节点,从S出发连接每个左部节点,原来的每条边看做从左部节点连接到右部节点的有向边,从每个右部节点出发连T,所有边的容量都为1。求出的最大流量就是二分图最大匹配数。
在允许多重匹配的情况下,可以将从S到左部节点的边容量设为匹配上限,右部节点到T的边容量设为匹配上限。

Edmonds-Karp 增广路算法

若一条从源点到汇点的路径上,各边剩余容量都大于0,则这条路是一条增广路。那么可以利用这条增广路使网络流增大。
增广路就是每次BFS寻找增广路,找到流量为e的增广路之后,就更新路径上每条正向边剩余流量e,反向边流量+e。直到找不到增广路,算法结束。
时间复杂度为O(nm2),实际使用远远达不到这个数值,可以处理103~104规模的图。
P3376 【模板】网络最大流

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 205;
const int MAXM = 5e5 + 10;
#define int long long
const int INF = INT_MAX;
int head[MAXN], ver[MAXM << 1], nxt[MAXM << 1], edge[MAXM << 1], tot = 1;
inline void add(const int &x, const int &y, const int &z)
{
	ver[++tot] = y;
	edge[tot] = z;
	nxt[tot] = head[x];
	head[x] = tot;
}
int n, m, s, t, maxflow;
int v[MAXN], incf[MAXN], pre[MAXN];
bool bfs()
{
	memset(v, 0, sizeof(v));
	queue<int> q;
	q.push(s);
	v[s] = 1;
	incf[s] = INF;//增广路上边的最小容量
	while(q.size())
	{
		int x = q.front();
		q.pop();
		for(int i = head[x]; i; i = nxt[i])
		{
			if(edge[i])
			{
				int y = ver[i];
				if(v[y])
					continue;
				incf[y] = min(incf[x], edge[i]);//更新最小容量
				pre[y] = i;//记录前驱,用于更新
				q.push(y);
				v[y] = 1;
				if(y == t)
					return true;
			}
		}
	}
	return false;
}
void update()
/*更新增广路上边和反向边的容量*/
{
	int x = t;
    //利用前驱遍历增广路
	while(x != s)
	{
		int i = pre[x];
		edge[i] -= incf[t];//正向边-e
		edge[i ^ 1] += incf[t];//反向边+e
		x = ver[i ^ 1];//利用了成对存储的技巧
	}
	maxflow += incf[t];
}
signed main()
{
	cin >> n >> m;
	cin >> s >> t;
	for(int i = 1; i <= m; ++i)
	{
		int x, y, z;
		cin >> x >> y >> z;
		add(x, y, z);
		add(y, x, 0);
	}
	while(bfs())
		update();
	cout << maxflow << endl;
}

Dinic算法

残量网络:在任意时刻,网络中所有节点以及剩余容量大于0的边构成的子图被称为残量网络,Edmonds-Karp每一次bfs遍历了整个残量网络,但是只找一个增广路,不优。
分层图:d[y]=d[x]+1的边(x,y)构成的子图就是一张分层图
Dinic算法过程如下:
1、bfs残量网络,构造分层图
2、dfs分层图,寻找增广路并更新剩余容量。优点在于dfs的时候可以同时寻找多条增广路,回溯时可以更新剩余容量。(dfs的时候有一些剪枝)
算法复杂度O(n2m),实际处理104~105的数据,并且在求解二分图最大匹配的时候复杂度为O(mn)

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int MAXN = 205;
const int MAXM = 5e3 + 10;
int head[MAXN], nxt[MAXM << 1], ver[MAXM << 1], edge[MAXM << 1], tot = 1;
int d[MAXN];
int n, m, s, t, maxflow;
inline void add(const int &x, const int &y, const int &z)
{
	ver[++tot] = y;
	edge[tot] = z;
	nxt[tot] = head[x];
	head[x] = tot;
}
bool bfs()
/*bfs构造分层图*/
{
	queue<int> q;
	memset(d, 0, sizeof(d));
	q.push(s);
	d[s] = 1;
	while(q.size())
	{
		int x = q.front();
		q.pop();
		for(int i = head[x]; i; i = nxt[i])
		{
			int y = ver[i];
			if(edge[i] && !d[y])
			{
				q.push(y);
				d[y] = d[x] + 1;
				if(y == t)
					return true;
			}
		}
	}
	return false;
}
int dinic(int x, int flow)
/*dfs寻找多条增广路*/
{
	if(x == t)
		return flow;
	int rest = flow, k;
	for(int i = head[x]; i && rest; i = nxt[i])
	{
		int y = ver[i];
		if(edge[i] && d[y] == d[x] + 1)
		{
			k = dinic(y, min(rest, edge[i]));
			if(!k)//后续没有增广路,直接从分层图中去掉该节点,剪枝
				d[ver[i]] = 0;
			edge[i] -= k;
			edge[i ^ 1] += k;
			rest -= k;
		}
	}
	return flow - rest;
}
signed main()
{
	cin >> n >> m >> s >> t;
	for(int i = 1; i <= m; ++i)
	{
		int x, y, z;
		cin >> x >> y >> z;
		add(x, y, z);
		add(y, x, 0);
	}
	int flow = 0;
	while(bfs())
		while(flow = dinic(s, LONG_LONG_MAX))
			maxflow += flow;
	cout << maxflow << endl;
}

其他例题:
P2740 [USACO4.2]草地排水Drainage Ditches

还有一些其它算法等待学习:

Ford-Fulkerson(FF):以为是很高级的其实是不分层图的Dinic(或者dfs的EK)
ISAP(Improved Shortest Augmenting Path,更优最短增广路径算法):优化了求分层图的过程
HLPP算法:不依赖增广路,是一种预流推进算法

posted @   椎名·六花  阅读(38)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
点击右上角即可分享
微信分享提示