【Coel.学习笔记】费用流的含义与基础运用
作者:互联网
基本含义
在一张流网络中,最大流是不唯一的。那么给每条边再加上一个费用值,所有最大流中费用和的极值就叫费用流。对应地,费用最小值为最小费用最大流,费用最大值为最大费用最大流。
算法内容
使用 EK 算法或 Dinic 算法,把 bfs 换成 SPFA 就可以求出最小费用最大流。
需要注意,当流网络上存在费用负环,那么最小费用最大流无法求出。这时需要使用消圈法删除负圈。
代码如下(使用 EK 算法):
// Problem: P3381 【模板】最小费用最大流
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3381
// Memory Limit: 128 MB
// Time Limit: 1000 ms
// Author: Coel
//
// Powered by CP Editor (https://cpeditor.org)
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
const int maxn = 5e5 + 10, inf = 1e8;
int n, m, S, T;
int head[maxn], nxt[maxn], to[maxn], c[maxn], w[maxn], cnt;
int d[maxn], pre[maxn], incf[maxn];
bool vis[maxn];
void add(int u, int v, int x, int y) {
nxt[cnt] = head[u], to[cnt] = v, c[cnt] = x, w[cnt] = y, head[u] = cnt++;
nxt[cnt] = head[v], to[cnt] = u, c[cnt] = 0, w[cnt] = -y, head[v] = cnt++;
}
bool spfa() {
queue<int> Q;
memset(d, 0x3f, sizeof(d));
memset(incf, 0, sizeof(incf));
Q.push(S), d[S] = 0, incf[S] = inf;
while (!Q.empty()) {
int u = Q.front();
Q.pop();
vis[u] = false;
for (int i = head[u]; ~i; i = nxt[i]) {
int v = to[i];
if (c[i] && d[v] > d[u] + w[i]) {
d[v] = d[u] + w[i];
pre[v] = i;
incf[v] = min(c[i], incf[u]);
if (!vis[v]) {
Q.push(v);
vis[v] = true;
}
}
}
}
return incf[T] > 0;
}
void Edmond_Karp(int& flow, int& cost) {
flow = cost = 0;
while (spfa()) {
int t = incf[T];
flow += t, cost += t * d[T];
for (int i = T; i != S; i = to[pre[i] ^ 1]) {
c[pre[i]] -= t;
c[pre[i] ^ 1] += t;
}
}
}
int main(void) {
ios::sync_with_stdio(false);
cin.tie(nullptr);
memset(head, -1, sizeof(head));
cin >> n >> m >> S >> T;
for (int i = 1; i <= m; i++) {
int u, v, x, y;
cin >> u >> v >> x >> y;
add(u, v, x, y);
}
int flow, cost;
Edmond_Karp(flow, cost);
cout << flow << ' ' << cost;
return 0;
}
实战应用
下面给出几个比较简单的例题。
网络流 24 题:运输问题
洛谷传送门
有若干个仓库和零售商店,每个仓库拥有一定货物,每个商店需要一定货物,保证仓库存放的货物量等于商店需要的货物量,从不同仓库运到不同商店的运输费用各不相同。求出运输费用的最大值和最小值。
解析:这有点像多源多汇问题,建立一个源点与仓库相连,一个汇点与商店相连,容量等于供需量,费用等于零;接下来给每个仓库和商店连边,容量正无穷,费用等于运输花费,这个问题就变成了最小费用最大流问题。
由于要同时输出最大值和最小值,这里可以在做完最大值之后还原网络并跑一边负费用,就不需要再把 spfa 再写一遍了。
// Problem: P4015 运输问题
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P4015
// Memory Limit: 250 MB
// Time Limit: 1000 ms
// Author: Coel
//
// Powered by CP Editor (https://cpeditor.org)
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
const int maxn = 5e4 + 10, inf = 1e8;
int n, m, S, T;
int head[maxn], nxt[maxn], to[maxn], c[maxn], w[maxn], cnt;
int dis[maxn], pre[maxn], incf[maxn];
bool vis[maxn];
void add(int u, int v, int x, int y) {
nxt[cnt] = head[u], to[cnt] = v, c[cnt] = x, w[cnt] = y, head[u] = cnt++;
nxt[cnt] = head[v], to[cnt] = u, c[cnt] = 0, w[cnt] = -y, head[v] = cnt++;
}
bool spfa() {
queue<int> Q;
memset(dis, 0x3f, sizeof(dis));
memset(incf, 0, sizeof(incf));
Q.push(S), dis[S] = 0, incf[S] = inf;
while (!Q.empty()) {
int u = Q.front();
Q.pop();
vis[u] = false;
for (int i = head[u]; ~i; i = nxt[i]) {
int v = to[i];
if (c[i] && dis[v] > dis[u] + w[i]) {
dis[v] = dis[u] + w[i];
pre[v] = i;
incf[v] = min(c[i], incf[u]);
if (!vis[v]) Q.push(v), vis[v] = true;
}
}
}
return incf[T] > 0;
}
int Edmond_Karp() {
int res = 0;
while (spfa()) {
int t = incf[T];
res += t * dis[T];
for (int i = T; i != S; i = to[pre[i] ^ 1])
c[pre[i]] -= t, c[pre[i] ^ 1] += t;
}
return res;
}
int main(void) {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> m >> n;
S = 0, T = m + n + 1;
memset(head, -1, sizeof(head));
for (int i = 1, x; i <= m; i++) {
cin >> x;
add(S, i, x, 0);
}
for (int i = 1, x; i <= n; i++) {
cin >> x;
add(m + i, T, x, 0);
}
for (int i = 1; i <= m; i++)
for (int j = 1; j <= n; j++) {
int cost;
cin >> cost;
add(i, m + j, inf, cost);
}
cout << Edmond_Karp() << '\n';
for (int i = 0; i < cnt; i += 2) {
c[i] += c[i ^ 1], c[i ^ 1] = 0;
w[i] = -w[i], w[i ^ 1] = -w[i ^ 1];
}
cout << -Edmond_Karp();
return 0;
}
网络流 24 题:负载平衡问题
转眼间网络流 24 题已经做完三分之一了……
洛谷传送门
有 \(n\) 个沿铁路运输线环形排列的仓库,每个仓库存储的货物数量不等。如何用最少搬运量可以使 \(n\) 个仓库的库存数量相同。搬运货物时,只能在相邻的仓库之间搬运。
解析:这题实际上是一个贪心题,贪心做法略,留给读者锻炼思维。
费用流做法的话,把仓库分成两大类:存储量大于平均数,存储量小于平均数。
类似上题,把存储量大的与源点相连,存储量小的与汇点相连,容量等于存储量与平均数之差的绝对值。再给相邻的仓库连边,容量正无穷,费用等于 1。
int main(void) {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
S = 0, T = n + 1;
memset(head, -1, sizeof(head));
for (int i = 1; i <= n; i++) {
cin >> a[i];
ave += a[i];
add(i, i < n ? i + 1 : 1, inf, 1);
add(i, i > 1 ? i - 1 : n, inf, 1);
}
ave /= n;
for (int i = 1; i <= n; i++)
if (a[i] > ave)
add(S, i, a[i] - ave, 0);
else if (a[i] < ave)
add(i, T, ave - a[i], 0);
cout << Edmond_Karp();
return 0;
}
标签:费用,cnt,int,含义,Coel,笔记,maxn,incf,head 来源: https://www.cnblogs.com/Coel-Flannette/p/16477163.html