「网络流 24 题」负载平衡
G 公司有 \(n\) 个沿铁路运输线环形排列的仓库,每个仓库存储的货物数量不等。如何用最少搬运量可以使 \(n\) 个仓库的库存数量相同。搬运货物时,只能在相邻的仓库之间搬运。
这道题我们建立一个超级源点和超级汇点,源点向每个仓库连边,容量为仓库库存,仓库向汇点连边,容量为库存平均值。每一个点向左右连边,边权为inf。
对于这些边,我们都需要建一条与他们方向相反,容量为 \(0\) ,费用为原边的相反数。
然后直接跑一下最小费用最大流,费用即为解。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
int read() {
int a = 0, x = 1;
char ch = getchar();
while (ch > '9' || ch < '0') {
if (ch == '-')
x = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
a = a * 10 + ch - '0';
ch = getchar();
}
return a * x;
}
const int N = 807, inf = 1e9 + 7;
;
int n, arr[N], sum = 0;
int s, t, pre1[N], pre2[N];
int head[N], go[N], nxt[N], val[N], lim[N], cnt = -1;
void add(int u, int v, int r, int w) {
go[++cnt] = v;
nxt[cnt] = head[u];
head[u] = cnt;
val[cnt] = w, lim[cnt] = r;
}
int dis[N], ins[N];
bool SPFA(int pos) {
memset(ins, 0, sizeof(ins));
queue<int> q;
for (int i = 1; i <= n + 1; i++) dis[i] = inf;
dis[pos] = 0, ins[pos] = 1;
q.push(pos);
while (!q.empty()) {
int u = q.front();
q.pop();
ins[u] = 0;
// printf("%d \n",u);
for (int e = head[u]; ~e; e = nxt[e]) {
int v = go[e];
if (!lim[e])
continue;
if (dis[v] > dis[u] + val[e]) {
dis[v] = dis[u] + val[e];
pre1[v] = u, pre2[v] = e;
if (!ins[v]) {
q.push(v);
ins[v] = 1;
}
}
}
}
return dis[t] != inf;
}
void print(int i) {
if (i == s)
printf("%d ", s);
else {
print(pre1[i]);
printf("%d ", i);
}
}
int main() {
n = read();
s = 0, t = n + 1;
memset(head, -1, sizeof(head));
memset(nxt, -1, sizeof(nxt));
for (int i = 1; i <= n; i++) {
arr[i] = read();
// add(0, i, arr[i], 0);
// add(i, 0, 0, 0);
sum += arr[i];
}
sum /= n;
for (int i = 1; i <= n; i++) {
if (arr[i] < sum) {
add(i, n + 1, sum - arr[i], 0);
add(n + 1, i, 0, 0);
} else {
add(0, i, arr[i] - sum, 0);
add(i, 0, 0, 0);
}
add(i, i % n + 1, inf, 1);
add(i % n + 1, i, 0, -1);
add(i % n + 1, i, inf, 1);
add(i, i % n + 1, 0, -1);
}
int ans = 0;
// printf("%d\n",sum);
while (SPFA(s)) {
/// printf("!");
int tmp = inf;
for (int i = t; i != s; i = pre1[i]) tmp = min(tmp, lim[pre2[i]]);
ans += dis[t] * tmp;
// print(t);
// putchar('\n');
for (int i = t; i != s; i = pre1[i]) lim[pre2[i]] -= tmp, lim[pre2[i] ^ 1] += tmp;
// printf("%d %d\n",tmp,ans);
}
printf("%d", ans);
return 0;
}