题意
某公司要处理一个周期性的物流问题。有 (n) 个城市,第 (i) 个城市在每周的第 (j(1leq j leq 7)) 天会生产 (a_{ij}) 吨某种货物,同时需要消耗 (b_{ij}) 吨该种货物。已知每周的产量等于消耗量(即 (a_{ij}) 之和等于 (b_{ij}) 之和)。
城市之间有 (m) 条道路,第 (k) 条道路连接了城市 (s_k) 和 (t_k)。一条道路上运输 (1) 吨货物有一个固定的成本 (c_k)。道路都可以双向使用。每天运输的货物量没有限制。城市之间的距离并不远,货物可以从任意一个城市运输到任意另一个城市并且在当天到达。
货物如果在当天没有被消耗掉,就需要存放在仓库里过夜。第 (i) 个城市的仓库容量为 (v_i),存放 (1) 吨货物过一夜所需的成本是 (w_i)。
请你计算该公司如果每周循环性地按照一个固定的流程调度货物的话,该公司在最优方案下每周需要为货物的运输和存储消耗多少成本。
思路
典型的拆点费用流问题,即将一个城市拆成(7)个点。
设立虚拟源点(S),向每个点连容量是(a_{ij}),费用是(0)的边;设立虚拟汇点(T),每个点向(T)连容量是(b_{ij}),费用是(0)的边。
因为每天的货物可以储存在仓库中留到下一天,因此第(i)个城市第(j)天,向第(j + 1)天连容量是(v),费用是(w)的边。
这里需要注意一点,就是因为是每周循环,因此第(7)天需要向第(1)天连边。
对于两个能互相到达的城市,每天互相连边,容量是(infty),费用是(c)。
跑一遍费用流即可。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 710, M = (500 * 14 + 10 * N) * 2, inf = 1e8;
int n, m, S, T;
int h[N], e[M], ne[M], f[M], w[M], idx;
int pre[N], d[N], incf[N];
bool st[N];
int a[8], b[8];
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(d, 0x3f, sizeof(d));
memset(incf, 0, sizeof(incf));
queue<int> que;
que.push(S);
d[S] = 0, incf[S] = inf;
st[S] = true;
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(d[ver] > d[t] + w[i] && f[i]) {
d[ver] = d[t] + w[i];
pre[ver] = i;
incf[ver] = min(f[i], incf[t]);
if(!st[ver]) {
que.push(ver);
st[ver] = true;
}
}
}
}
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 get(int id, int day)
{
return (id - 1) * 7 + day;
}
int main()
{
scanf("%d%d", &n, &m);
S = 0, T = 7 * n + 1;
memset(h, -1, sizeof(h));
for(int i = 1; i <= n; i ++) {
int v, w;
for(int j = 1; j <= 7; j ++) scanf("%d", &a[j]);
for(int j = 1; j <= 7; j ++) scanf("%d", &b[j]);
scanf("%d%d", &v, &w);
for(int j = 1; j <= 7; j ++) {
add(S, get(i, j), a[j], 0);
add(get(i, j), T, b[j], 0);
if(j < 7) add(get(i, j), get(i, j + 1), v, w);
else add(get(i, j), get(i, 1), v, w);
}
}
while(m --) {
int u, v, c;
scanf("%d%d%d", &u, &v, &c);
for(int i = 1; i <= 7; i ++) {
add(get(u, i), get(v, i), inf, c);
add(get(v, i), get(u, i), inf, c);
}
}
printf("%d
", EK());
return 0;
}