网络流24题- 餐巾计划 (最小费用最大流)
题意:
一个餐厅在相继的 n 天里,每天需用的餐巾数不尽相同。假设第 i 天需要 ri块餐巾。餐厅可以购买新的餐巾,每块餐巾的费用为P,
或者把旧餐巾送到快洗部,洗一块需 M 天,其费用为 F 分;或者送到慢洗部,洗一块需 N 天,其费用为 S 分(S<F)。
每天结束时,餐厅必须决定将多少块脏的餐巾送到快洗部,多少块餐巾送到慢洗部,以及多少块保存起来延期送洗。但是每天洗好的餐巾和购买的新餐巾数之和,要满足当天的需求量。
试设计一个算法为餐厅合理地安排好 n 天中餐巾使用计划,使总的花费最小。编程找出一个最佳餐巾使用计划。
链接: https://loj.ac/problem/6008
输入:
第1行有6个整数,n,P,M,F,S,N,含义与上面相对应,
接下来的n行是每天需要的餐巾数
输出格式:
将餐厅在相继的 n 天里使用餐巾的最小总花费输出。
思路:
建图方式:
将 i 点拆成 i1 和 i2 ,其中 i1 代表每天的餐巾数,i2 代表每天可以得到的餐巾。
将源点 S 与 i1 相连,容量为所需要的餐巾数,费用为0,将 i2 与汇点 T 相连,容量为所需要的餐巾数,费用为0
由于每天可以购买新的餐巾,费用为P,所以将 S 与 i2 相连,容量为INF,费用为P
用掉的脏餐巾可以累积,所以对于每个 i1 < n ,连接 i1-1 与 i1 ,容量为INF,费用为0
每天得到的餐巾可以由快洗或者慢洗得到,所以将每个 i1 与若干天后他能清洗完送达到的那个 i2 相连,容量为INF,费用为快洗或慢洗所需的费用
然后再跑一遍最小费用最大流
代码:
写法1:快点
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <queue> using namespace std; const int MAXN = 2e5 + 5; const int INF = 0x7fffffff; typedef long long ll; int head[MAXN], tot, s, t, n; struct node { int to, nxt, flow, cost; } e[MAXN]; void add(int x, int y, int z, int c) { e[tot].to = y; e[tot].nxt = head[x]; e[tot].flow = z; e[tot].cost = c; head[x] = tot++; } void add_edge(int x, int y, int z, int c) { add(x, y, z, c); add(y, x, 0, -c); } int dis[MAXN]; bool visit[MAXN]; int cost = 0; bool spfa() { queue<int> q; memset(visit, 0, sizeof(visit)); for (int i = 0; i <= 2 * n + 1; i++) dis[i] = INF; dis[s] = 0; q.push(s); while (q.size()) { int u = q.front(); q.pop(); visit[u] = 0; for (int i = head[u]; ~i; i = e[i].nxt) { int v = e[i].to; if (dis[v] > dis[u] + e[i].cost && e[i].flow) { dis[v] = dis[u] + e[i].cost; if (!visit[v]) { q.push(v); visit[v] = 1; } } } } return dis[t] != INF; } int dfs(int u, int flow) { if (u == t) { visit[t] = 1; return flow; } int f = 0; visit[u] = 1; for (int i = head[u]; ~i; i = e[i].nxt) { int v = e[i].to; if ((!visit[v] || v == t) && e[i].flow && dis[v] == dis[u] + e[i].cost) { int d = dfs(v, min(flow - f, e[i].flow)); if (d) { cost += e[i].cost * d; e[i].flow -= d; e[i ^ 1].flow += d; f += d; if (f == flow) break; } } } return f; } void mcmf() { while (spfa()) { // printf("*"); visit[t] = 1; while (visit[t]) { memset(visit, 0, sizeof(visit)); dfs(s, INF); } } } int main() { int p, f_m, f_p, s_n, s_p; scanf("%d%d%d%d%d%d", &n, &p, &f_m, &f_p, &s_n, &s_p); memset(head, -1, sizeof(head)); s = 0, t = 2 * n + 1; for (int i = 1; i <= n; i++) { int need; scanf("%d", &need); add_edge(s, i, need, 0); add_edge(i + n, t, need, 0); } for (int i = 1; i <= n; i++) { add_edge(s, i + n, INF, p); } for (int i = 1; i < n; i++) add_edge(i, i + 1, INF, 0); for (int i = 1; i + f_m <= n; i++) add_edge(i, i + f_m + n, INF, f_p); for (int i = 1; i + s_n <= n; i++) add_edge(i, i + n + s_n, INF, s_p); mcmf(); printf("%d\n", cost); return 0; }
写法2:慢点
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <queue> using namespace std; const int MAXN = 2e5 + 5; const int INF = 0x7fffffff; typedef long long ll; int head[MAXN], tot, s, t, n, a[200][200]; struct node { int to, nxt, flow, cost; } e[MAXN]; void add(int x, int y, int z, int c) { e[tot].to = y; e[tot].nxt = head[x]; e[tot].flow = z; e[tot].cost = c; head[x] = tot++; } void add_edge(int x, int y, int z, int c) { add(x, y, z, c); add(y, x, 0, -c); } int dis[MAXN], q[MAXN], l, r, pre[MAXN], path[MAXN], visit[MAXN]; bool spfa() { for (int i = 0; i <= 2 * n + 1; i++) dis[i] = INF; for (int i = 0; i <= 2 * n + 1; i++) pre[i] = -1; l = 0, r = 0; dis[s] = 0; q[++r] = s; while (l != r) { int u = q[++l]; for (int i = head[u]; ~i; i = e[i].nxt) { int v = e[i].to; if (e[i].flow && dis[u] + e[i].cost < dis[v]) { dis[v] = dis[u] + e[i].cost; pre[v] = u, path[v] = i; if (!visit[v]) q[++r] = v, visit[v] = 1; } } visit[u] = 0; } if (pre[t] == -1) return false; return true; } int mcmf() { int f = 0, cost = 0; while (spfa()) { int min_f = INF; for (int i = t; i != s; i = pre[i]) { if (e[path[i]].flow < min_f) min_f = e[path[i]].flow; } f += min_f; cost += dis[t] * min_f; for (int i = t; i != s; i = pre[i]) { e[path[i]].flow -= min_f; e[path[i] ^ 1].flow += min_f; } } return cost; } int main() { int p, f_m, f_p, s_n, s_p; scanf("%d%d%d%d%d%d", &n, &p, &f_m, &f_p, &s_n, &s_p); memset(head, -1, sizeof(head)); s = 0, t = 2 * n + 1; for (int i = 1; i <= n; i++) { int need; scanf("%d", &need); add_edge(s, i, need, 0); add_edge(i + n, t, need, 0); } for (int i = 1; i <= n; i++) { add_edge(s, i + n, INF, p); } for (int i = 1; i < n; i++) add_edge(i, i + 1, INF, 0); for (int i = 1; i + f_m <= n; i++) add_edge(i, i + f_m + n, INF, f_p); for (int i = 1; i + s_n <= n; i++) add_edge(i, i + n + s_n, INF, s_p); printf("%d\n", mcmf()); return 0; }