「网络流 24 题」餐巾计划
思路
我们先建立超级源点 S S S 和超级汇点 T T T,对于每一天,我们将其拆分成两个点 A i A_i Ai 和 B i B_i Bi,其中 A i A_i Ai 表示这一天实际消耗的餐巾,连边 S → ∞ A i S \stackrel{\infty} \rarr A_i S→∞Ai 容量无穷大,费用为 P P P,表示新购买餐巾; A i → x i T A_i \stackrel{x_i} \rarr T Ai→xiT,容量 x i x_i xi,费用为 0 0 0,其中 x i x_i xi 表示这一天需要的餐巾数
连边 S → x i B i S \stackrel{x_i} \rarr B_i S→xiBi,容量为 x i x_i xi,费用为 0 0 0,用以限制这一天用完后的餐巾数量送去洗过后,后面重复使用。
连边
B
i
→
A
i
+
M
B_i \rarr A_{i + M}
Bi→Ai+M,容量为
∞
\infty
∞,费用为
F
F
F
连边
B
i
→
A
i
+
N
B_i \rarr A_{i + N}
Bi→Ai+N,容量为
∞
\infty
∞,费用为
S
S
S
连边
B
i
→
B
i
+
1
B_i \rarr B_{i + 1}
Bi→Bi+1,容量为
∞
\infty
∞,费用为
0
0
0
以上这一部分的连边表示:把当前这一天用完的脏的餐巾送去洗,用以后续使用,注意前面
S
→
x
i
B
i
S \stackrel{x_i} \rarr B_i
S→xiBi 已经限制了这一天送去洗的餐巾数量。而洗完后的餐巾可能不会立即被使用,所以我们有一个传递
B
i
→
B
i
+
1
B_i \rarr B_{i + 1}
Bi→Bi+1,表示延迟洗,其实也等价于先洗好然后等待,这两种方式是等价的
#include<bits/stdc++.h>
#define fore(i,l,r) for(int i=(int)(l);i<(int)(r);++i)
#define fi first
#define se second
#define endl '\n'
#define ull unsigned long long
#define ALL(v) v.begin(), v.end()
#define Debug(x, ed) std::cerr << #x << " = " << x << ed;
const int INF=0x3f3f3f3f;
const long long INFLL=1e18;
typedef long long ll;
struct MCF {
struct Edge {
int v, c, w; //边终点、容量、费用
Edge(int v, int c, int w) : v(v), c(c), w(w) {}
};
const int n;
std::vector<Edge> e;
std::vector<std::vector<int>> g;
std::vector<ll> h, dis;
std::vector<int> pre;
bool dijkstra(int s, int t) {
dis.assign(n + 1, std::numeric_limits<ll>::max());
pre.assign(n + 1, -1);
std::priority_queue<std::pair<ll, int>, std::vector<std::pair<ll, int>>, std::greater<std::pair<ll, int>>> que;
dis[s] = 0;
que.emplace(0, s);
while (!que.empty()) {
ll d = que.top().first;
int u = que.top().second;
que.pop();
if (dis[u] < d) continue;
for (int i : g[u]) {
int v = e[i].v;
int c = e[i].c;
int w = e[i].w;
if (c > 0 && dis[v] > d + h[u] - h[v] + w) {
dis[v] = d + h[u] - h[v] + w;
pre[v] = i;
que.emplace(dis[v], v);
}
}
}
return dis[t] != std::numeric_limits<ll>::max();
}
MCF(int n) : n(n), g(n + 1) {}
void addEdge(int u, int v, int c, int w) {
g[u].push_back(e.size());
e.emplace_back(v, c, w);
g[v].push_back(e.size());
e.emplace_back(u, 0, -w);
}
std::pair<int, ll> flow(int s, int t) {
int flow = 0;
ll cost = 0;
h.assign(n + 1, 0);
while (dijkstra(s, t)) {
for (int i = 1; i <= n; ++i) h[i] += dis[i];
int aug = std::numeric_limits<int>::max();
for (int i = t; i != s; i = e[pre[i] ^ 1].v) aug = std::min(aug, e[pre[i]].c);
for (int i = t; i != s; i = e[pre[i] ^ 1].v) {
e[pre[i]].c -= aug;
e[pre[i] ^ 1].c += aug;
}
flow += aug;
cost += ll(aug) * h[t];
}
return std::make_pair(flow, cost);
}
};
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int n, P, M, F, N, S;
std::cin >> n >> P >> M >> F >> N >> S;
std::vector<int> a(n + 1);
MCF mcf(2 * n + 2);
int s = 2 * n + 1, t = s + 1;
fore(i, 1, n + 1){
int in = 2 * i - 1, out = 2 * i;
std::cin >> a[i];
mcf.addEdge(s, in, a[i], 0);
mcf.addEdge(out, t, a[i], 0);
mcf.addEdge(s, out, INF, P);
if(i + M <= n) mcf.addEdge(in, 2 * (i + M), INF, F);
if(i + N <= n) mcf.addEdge(in, 2 * (i + N), INF, S);
if(i > 1) mcf.addEdge(in - 2, in, INF, 0);
}
auto [flow, cost] = mcf.flow(s, t);
std::cout << cost;
return 0;
}