一个餐厅在相继的 N 天里,每天需用的餐巾数不尽相同。
假设第 i 天需要 ri 块餐巾 (i=1,2,…,N)。
餐厅可以购买新的餐巾,每块餐巾的费用为 p 分;或者把旧餐巾送到快洗部,洗一块需 m 天,其费用为 f 分;或者送到慢洗部,洗一块需 n 天,其费用为 s 分。
餐厅每天使用的餐巾必须是今天刚购买的,或者是今天刚洗好的,且必须恰好提供 ri 块毛巾,不能多也不能少。
每天结束时,餐厅必须决定将多少块脏的餐巾送到快洗部,多少块餐巾送到慢洗部,以及多少块保存起来延期送洗。
但是每天洗好的餐巾和购买的新餐巾数之和,要满足当天的需求量。
试设计一个算法为餐厅合理地安排好 N 天中餐巾使用计划,使总的花费最小。
输入格式
第 1 行有 6 个正整数 N,p,m,f,n,s。N 是要安排餐巾使用计划的天数;p 是每块新餐巾的费用;m 是快洗部洗一块餐巾需用天数;f 是快洗部洗一块餐巾需要的费用;n 是慢洗部洗一块餐巾需用天数;s 是慢洗部洗一块餐巾需要的费用。
接下来的 N 行是餐厅在相继的 N 天里,每天需用的餐巾数。
输出格式
输出餐厅在相继的 N 天里使用餐巾的最小总花费。
数据范围
1≤N≤800
1≤s<f<p≤50,
1≤m≤n≤20,
每天需用的餐巾数不超过 1000。
输入样例:
3 10 2 3 3 2
5
6
7
输出样例:
145
解析:
每天的毛巾有三种来处,新买、快洗、慢洗,也有三种去处,放着、快洗、慢洗,放着不洗是不能直接用的。
可以发现,每天我们都需要接收一些毛巾,也需要输出一些毛巾,如果把每一天看作一个节点,那么每个节点都会有两种操作,因此我们可以对每个点进行拆点操作。
将第 i 天分成两个点,一个点 i1 表示用完的旧毛巾,一个点 i2 表示需要的新毛巾。
第 i 天需要的新毛巾有三种来处,新买、快洗、慢洗。新买相当于凭空变出毛巾,从源点向 i2 连一条边,容量是 +∞,费用是 p;快洗则是从第 i−m 天的旧毛巾中洗出来的,因此从 (i−m)1 向 i2 连一条边,容量是 +∞,费用是 f;慢洗则是从第 i−n 天的旧毛巾中洗出来的,因此从 (i−n)1 向 i2 连一条边,容量是 +∞,费用是 s。本题中第 i 天固定只需要 ri 条毛巾,因此从 i2 向汇点连一条容量是 ri 的边,费用是 0。
第 i 天用完的毛巾也有三种去处,留着、快洗、慢洗。留着相当于将第 i 天的旧毛巾放到了第 i+1 天,因此从 i1 向 (i+1)1 连一条边,容量是 +∞,费用是 0;快洗则是将第 i 天的旧毛巾洗了给第 i+m 天,因此从 i1 向 (i+m)2 连一条边,容量是 +∞,费用是 f;慢洗则是将第 i 天的旧毛巾洗了给第 i+n 天,因此从 i1 向 (i+n)2 连一条边,容量是 +∞,费用是 s。第 i 天还会产生 ri 条旧毛巾,因此从源点向 i1 连一条边,容量是 ri,费用是 0。
通过以上方式就能构建出一个流网络,可以发现原问题中任意一个合理方案都能对应到流网络中任意一个可行流,并且由于到汇点的流量之和是 ∑ri,所以任意一个合理方案对应到流网络中都是最大流。然后我们还能进一步发现原问题的任意一个方案的总费用和流网络中任意一个最大流的总费用是一一对应的,这里的结论可以自行证明。
综上所述,原问题的最小费用,就是流网络里所有最大流中费用最小的一个,即最小费用最大流。
这里如果不使用拆点的方式,可以发现原问题所对应的图的流量并不守恒,因此无法使用网络流算法。
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<math.h>
#include<map>
#include<sstream>
#include<deque>
#include<unordered_map>
#include<unordered_set>
#include<bitset>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
const int N = 800*2 + 10, M = (N * 3) * 2 + 10, INF = 0x3f3f3f3f;
int n, p, x, xp, y, yp,S,T;
int h[N], e[M], f[M], w[M], ne[M], idx;
int q[N], 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() {
int hh = 0, tt = 1;
memset(d, 0x3f, sizeof d);
memset(incf, 0, sizeof incf);
q[0] = S, d[S] = 0, incf[S] = INF;
while (hh != tt) {
int t = q[hh++];
if (hh == N)hh = 0;
st[t] = 0;
for (int i = h[t]; i != -1; i = ne[i]) {
int j = e[i];
if (f[i] && d[j] > d[t] + w[i]) {
d[j] = d[t] + w[i];
pre[j] = i;
incf[j] = min(incf[t], f[i]);
if (!st[j]) {
st[j] = 1;
q[tt++] = j;
if (tt == N)tt = 0;
}
}
}
}
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() {
cin >> n >> p >> x >> xp >> y >> yp;
memset(h, -1, sizeof h);
S = 0, T = n * 2 + 1;
for (int i = 1,a; i <= n; i++) {
scanf("%d", &a);
add(S, i, a, 0);
if (i < n)add(i, i + 1, INF, 0);
if (i + x <= n)add(i, i + x + n, INF, xp);
if (i + y <= n)add(i, i + y + n, INF, yp);
add(S, i + n, a, p);
add(i + n, T, a, 0);
}
printf("%d\n", EK());
return 0;
}