2184. 餐巾计划问题(网络流,费用流,拆点)#困难,想不到

文章介绍了一种算法,用于解决餐厅在N天内如何合理安排餐巾购买、快洗和慢洗,以最小化总花费的问题,通过构建流网络模型并找到最小费用最大流来实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

活动 - AcWing

一个餐厅在相继的 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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值