网络流

算法进阶课(试听课)—— 网络流的基本概念_哔哩哔哩_bilibili

概念:

源点:只有流出去的点
汇点:只有流进来的点
流量:一条边上流过的流量
容量:一条边上可供流过的最大流量
残量:一条边上的容量-流量

流:指满足网络流的一种方案。

1,在一个有向图中,有n个点和m条边。有一个源点,和一个汇点,每条边有一个容量限制。

2,每条边的流量<=容量。

3,源点流出量=汇点流入量,其余各点流入量和流出量相等


当不存在增广路时,当前流为最大流。

P3376 【模板】网络最大流 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

EK算法:

采用连事前向星的方式存储。

struct EDGE
{
	int v, power, nxt;
};
EDGE edge[10004];
int tot = 0, head[202];
int n, m, s, t, book[202], pre[202];
int d[202], M = 2147483647;
int bfs();
void add(int u, int v, int w);
void EK();

int main()
{
	cin >> n >> m >> s >> t;
	int x, y, z;
	for (int i = 1; i <= n; i++)
		head[i] = -1;
	for (int i = 1; i <= m; i++)
	{
		cin >> x >> y >> z;
		add(x, y, z);
        add(x,y,0);
	}
	EK();
	return 0;
}
void add(int u, int v, int w)
{
	edge[tot].v = v, edge[tot].power = w, edge[tot].nxt = head[u], head[u] = tot, tot++;
}
void EK()
{
	long long ans = 0;
	while (bfs())
	{
		ans = ans + d[t];
		for (int i = t; i != s; i = edge[pre[i] ^ 1].v)
		{
			edge[pre[i]].power -= d[t];
			edge[pre[i] ^ 1].power += d[t];
		}
	}
	cout << ans;
}
int bfs()//bfs寻找增广路
{
	queue<int> q;
	memset(book, 0, sizeof(book));
	memset(pre, 0, sizeof(pre));
	memset(d, 0, sizeof(d));
	q.push(s); book[s] = 1;
	d[s] = M;
	while (!q.empty())
	{
		int now = q.front(); q.pop();
		for (int i = head[now]; i != -1; i = edge[i].nxt)
		{
			int v = edge[i].v, w = edge[i].power;
			if (book[v] == 0 && w > 0)
			{
				q.push(v);
				book[v] = 1;
				d[v] = min(d[now], w);
				pre[v] = i;
				if (v == t)
					return 1;
			}
		}
	}
	return 0;
}

dinic算法:

对EK算法进行一些优化。

EK算法每次bfs只能找到一条增广路,

dinic算法利用分层图的原理,使用bfs将n个点分层,在使用dfs一次可以找到多条增广路。

#include<iostream>
#include<cmath>
#include<string>
#include<deque>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;


struct EDGE
{
	int v, power, nxt;
};
EDGE edge[10004];
int tot = 0, head[202], n, m, s, t, dep[202], M = 2147483647;
long long ans;

void add(int x, int y, int z);
void dinic();
int bfs();
int dfs(int x, int mi);
int main()
{
	cin >> n >> m >> s >> t;
	int x, y, z;
	memset(head, -1, sizeof(head));
	for (int i = 1; i <= m; i++)
	{
		cin >> x >> y >> z;
		add(x, y, z);
        add(y,x,0);
	}
	dinic();
	return 0;
}
void add(int x, int y, int z)
{
	edge[tot].v = y; edge[tot].power = z; edge[tot].nxt = head[x]; head[x] = tot; tot++;
}
void dinic()
{
	long long ans = 0;
	while (bfs())
	{
		ans = ans + dfs(s, M);
	}
	cout << ans;
}
int bfs()
{
	memset(dep, 0, sizeof(dep));
	dep[s] = 1;
	queue<int> q;
	q.push(s);
	while (!q.empty())
	{
		int now = q.front(); q.pop();
		for (int i = head[now]; i != -1; i = edge[i].nxt)
		{
			if (edge[i].power > 0 && dep[edge[i].v] == 0)
			{
				dep[edge[i].v] = dep[now] + 1;
				q.push(edge[i].v);
			}
		}
	}
	if (dep[t] == 0)
		return 0;
	return 1;
}
int dfs(int x, int mi)//x表示当前节点,mi表示节点s到节点x的所有路径的最小容量
{
	if (x == t)
		return mi;
	int out = 0;
	for (int i = head[x]; i != -1; i = edge[i].nxt)
	{
		if (dep[edge[i].v] == dep[x] + 1 && edge[i].power > 0)
		{
			int d = dfs(edge[i].v, min(mi, edge[i].power));
			mi -= d;
			out += d;
			edge[i].power -= d;
			edge[i ^ 1].power += d;
		}
	}
	if (out == 0)
		dep[x] = 0;
	return out;
}

最小费用最大流

P3381 【模板】最小费用最大流 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

和网络流类似。每条边再增加一个量:费用。

求网络流最大时的最小费用。


使用EK算法+SPFA优化可以解决。

EK算法求网络流时,求的是一条增广路,而不是花费最小的路。

使用SPFA代替bfs可以得到花费最小的增广路。

#include<iostream>
#include<cmath>
#include<string>
#include<deque>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;

const int Maxm = 5e4 + 4;
const int Maxn = 5e3 + 3;
struct EDGE
{
	int v, power, nxt, cost;
};
EDGE edge[Maxm * 2];
int tot, n, m, s, t, head[Maxn], Max = 2147483647, dis[Maxn], book[Maxn], flow[Maxn];
int pre[Maxn];

void add(int x, int y, int w, int c);
void MCMF();
int SPFA();
int main()
{
	cin >> n >> m >> s >> t;
	int x, y, w, c;
	memset(head, -1, sizeof(head));
	for (int i = 1; i <= m; i++)
	{
		cin >> x >> y >> w >> c;
		add(x, y, w, c);
	}
        //n表示有n个点,从1-n
	MCMF();
	return 0;
}
void add(int x, int y, int w, int c)
{
	edge[tot].v = y; edge[tot].power = w; edge[tot].cost = c; edge[tot].nxt = head[x]; head[x] = tot; tot++;
        edge[tot].v = x; edge[tot].power = 0; edge[tot].cost = -c; edge[tot].nxt = head[y]; head[y] = tot; tot++;
}
void MCMF()
{
	int ansflow = 0, anscost = 0;
	while (SPFA())
	{
		ansflow += flow[t];
		anscost += flow[t] * dis[t];
		for (int i = t; i != s; i = edge[pre[i] ^ 1].v)
		{
			edge[pre[i]].power -= flow[t];
			edge[pre[i] ^ 1].power += flow[t];
		}
	}
	cout << ansflow << " " << anscost;
}
int SPFA()
{
	queue<int> q;
	memset(book, 0, sizeof(book));
	memset(flow, 0, sizeof(flow));
	for (int i = 1; i <= n; i++)
		dis[i] = Max;
	q.push(s);
	book[s] = 1;
	flow[s] = Max;
	dis[s] = 0;
	//dis[i]表示s到i的最短路径,(费用之和最小)
	while (!q.empty())
	{
		int now = q.front(); q.pop();
		book[now] = 0;
		for (int i = head[now]; i != -1; i = edge[i].nxt)
		{
			int v = edge[i].v;
			if (edge[i].power > 0 && dis[v] > dis[now] + edge[i].cost)
			{
				dis[v] = dis[now] + edge[i].cost;
				flow[v] = min(flow[now], edge[i].power);
				pre[v] = i;
				if (book[v] == 0)
					book[v] = 1, q.push(v);
			}
		}
	}
	if (dis[t] == Max)
		return 0;
	return 1;
}
posted on 2022-04-07 13:38  naiji  阅读(163)  评论(0编辑  收藏  举报