1. 程式人生 > 遊戲資訊 >嗨,夥伴。 發件人:埃洛伊

嗨,夥伴。 發件人:埃洛伊

網路流

演算法進階課(試聽課)—— 網路流的基本概念_嗶哩嗶哩_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);
		add(y, x, 0, -c);
	}
	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++;
}
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;
}