题意
有 (n) 个沿铁路运输线环形排列的仓库,每个仓库存储的货物数量(a_i)不等。
如何用最少搬运量可以使 (n) 个仓库的库存数量相同。搬运货物时,只能在相邻的仓库之间搬运。
数据保证一定有解。
思路
这道题与运输问题有一些相似点。可以将这些仓库分成两类,一类是比最终数量多的仓库,另一类是比最终数量少的仓库。
最终数量为所有仓库库存数量的均值,即(tot = sum_{i = 1}^n a_i / n)
比最终数量多的仓库要运输出去的量其实就是(a_i - tot);比最终数量少的仓库要运输出去的量是(tot - a_i)。
设立虚拟源点(S),与比最终数量多的仓库连容量是(a_i - tot),费用是(0)的边;
设立虚拟汇点(T),比最终数量少的仓库与(T)连容量是(tot - a_i),费用是(0)的边。
因为一开始不够的仓库可能在中间过程中超过了目标容量,因此也有可能需要向旁边的仓库输出库存。
每个仓库向旁边两个仓库,连容量是(infty),费用是(1)的边。
跑最小费用流即可。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 110, M = 610, inf = 1e8;
int n, S, T;
int s[N];
int h[N], e[M], ne[M], f[M], w[M], idx;
int d[N], pre[N], incf[N];
bool st[N];
void add(int a, int b, int c, int d)
{
e[idx] = b, f[idx] = c, w[idx] = d, ne[idx] = h[a], h[a] = idx ++;
e[idx] = a, f[idx] = 0, w[idx] = -d, ne[idx] = h[b], h[b] = idx ++;
}
bool spfa()
{
memset(incf, 0, sizeof(incf));
memset(d, 0x3f, sizeof(d));
queue<int> que;
que.push(S);
st[S] = true;
d[S] = 0, incf[S] = inf;
while(que.size()) {
int t = que.front();
que.pop();
st[t] = false;
for(int i = h[t]; ~i; i = ne[i]) {
int ver = e[i];
if(f[i] && d[ver] > d[t] + w[i]) {
d[ver] = d[t] + w[i];
pre[ver] = i;
incf[ver] = min(f[i], incf[t]);
if(!st[ver]) {
st[ver] = true;
que.push(ver);
}
}
}
}
return incf[T] > 0;
}
int EK()
{
int cost = 0;
while(spfa()) {
int t = incf[T];
cost += t * d[T];
for(int i = T; i != S; i = e[pre[i] ^ 1]) {
f[pre[i]] -= t;
f[pre[i] ^ 1] += t;
}
}
return cost;
}
int main()
{
scanf("%d", &n);
memset(h, -1, sizeof(h));
S = 0, T = n + 1;
int tot = 0;
for(int i = 1; i <= n; i ++) {
int x;
scanf("%d", &x);
s[i] = x;
tot += x;
}
tot /= n;
for(int i = 1; i <= n; i ++) {
if(tot <= s[i]) add(S, i, s[i] - tot, 0);
else add(i, T, tot - s[i], 0);
}
for(int i = 1; i <= n; i ++) {
add(i, i + 1 <= n ? i + 1 : 1, inf, 1);
add(i, i - 1 >= 1 ? i - 1 : n, inf, 1);
}
printf("%d
", EK());
return 0;
}