洛谷传送门
题目描述
一个餐厅在相继的 N N 天里,每天需用的餐巾数不尽相同。假设第 天需要 ri r i 块餐巾 (i=1,2,...,N) ( i = 1 , 2 , . . . , N ) 。餐厅可以购买新的餐巾,每块餐巾的费用为 p p 分;或者把旧餐巾送到快洗部,洗一块需 天,其费用为 f f 分;或者送到慢洗部,洗一块需 天( n>m n > m ),其费用为 s s 分( )。
每天结束时,餐厅必须决定将多少块脏的餐巾送到快洗部,多少块餐巾送到慢洗部,以及多少块保存起来延期送洗。但是每天洗好的餐巾和购买的新餐巾数之和,要满足当天的需求量。
试设计一个算法为餐厅合理地安排好 N N 天中餐巾使用计划,使总的花费最小。编程找出一个最佳餐巾使用计划。
输入输出格式
输入格式:
由标准输入提供输入数据。文件第 行有 1 1 个正整数 ,代表要安排餐巾使用计划的天数。
接下来的 N N 行是餐厅在相继的 天里,每天需用的餐巾数。
最后一行包含 5 5 个正整数 。 p p 是每块新餐巾的费用; 是快洗部洗一块餐巾需用天数; f f 是快洗部洗一块餐巾需要的费用; 是慢洗部洗一块餐巾需用天数; s s 是慢洗部洗一块餐巾需要的费用。
输出格式:
将餐厅在相继的 天里使用餐巾的最小总花费输出
输入输出样例
输入样例#1:
3
1 7 5
11 2 2 3 1
输出样例#1:
134
说明
N<=2000 N <= 2000
ri<=10000000 r i <= 10000000
p,f,s<=10000 p , f , s <= 10000
解题分析
费用流的经典模型。
考虑如何建边:我们显然不能像题目所述的一样, 将白天的点又连回晚上的点, 这样就会混淆退流边和表示餐巾清洗的边,并且费用加在哪里不好确定。
因此我们将图建成二分图模型,连以下 6 6 类边:
- 源点向每天白天连费用为
p
p
, 流量为的边, 表示每天白天可以买新的餐巾。
- 源点 S S 向每天晚上连费用为,流量为当天餐巾使用量的边, 表示每天需要使用的餐巾在晚上变成了旧餐巾,需要清洗。
- 每天晚上向第二天晚上连费用为 0 0 ,流量为的边, 表示第一天的餐巾可以留到第二天。(其实还有一个作用:保证源点流入的流量始终和流出的流量平衡。如果我们不这样连的话即意味着第 i i 天的餐巾必须洗完后第或 i+n i + n 天使用。当然也可以在每天早上向第二天早上连边, 效果相同。)
- 第 i i 天晚上向第天早上连费用为 f f ,流量为的边, 表示第 i i 天晚上快洗的餐巾第天早上可以使用。
- 第 i i 天晚上向第天早上连费用为 f f ,流量为的边, 表示第 i i 天晚上慢洗的餐巾第天早上可以使用。
- 每天白天向汇点 T T 连费用为,流量为当天餐巾使用量的边,表示每天白天需要这么多的餐巾。
这样我们一路增广至最大流时即可满足。
代码如下:
#include <cstdio> #include <cmath> #include <cstring> #include <algorithm> #include <cstdlib> #include <cctype> #define R register #define IN inline #define W while #define gc getchar() #define MX 100050 #define S 50000 #define T 50001 #define INF 999999999 template <class TT> IN void in(TT &x) { x = 0; R char c = gc; W (!isdigit(c)) c = gc; W (isdigit(c)) x = (x << 1) + (x << 3) + c - 48, c = gc; } struct Edge { int to, flow, cost, nex; }edge[MX << 1]; int slow, fast, day, cnt = -1, nw, fc, sc, hd, tl; long long ans; int head[MX], rec[MX], del[MX], que[MX], dis[MX]; bool inq[MX]; IN void add(const int &from, const int &to, const int &cost, const int &fl) { edge[++cnt] = {to, fl, cost, head[from]}; head[from] = cnt; edge[++cnt] = {from, 0, -cost, head[to]}; head[to] = cnt; } IN bool SPFA() { R int now; que[tl = hd = 1] = S; std::memset(dis, 63, sizeof(dis)); del[S] = INF; dis[S] = 0; W (tl <= hd) { now = que[tl++]; for (R int i = head[now]; ~i; i = edge[i].nex) { if(edge[i].flow > 0 && dis[edge[i].to] > dis[now] + edge[i].cost) { del[edge[i].to] = std::min(edge[i].flow, del[now]); dis[edge[i].to] = dis[now] + edge[i].cost; rec[edge[i].to] = i; if(!inq[edge[i].to]) inq[edge[i].to] = true, que[++hd] = edge[i].to; } } inq[now] = false; } return dis[T] < 99999999; } IN void updata() { ans += del[T] * dis[T]; R int now = T, tar; W (now != S) { tar = rec[now]; edge[tar].flow -= del[T]; edge[tar ^ 1].flow += del[T]; now = edge[tar ^ 1].to; } } long long EK() { W (SPFA()) updata(); return ans; } int main(void) { in(day); int a, b; std::memset(head, -1, sizeof(head)); for (R int i = 1; i <= day; ++i) in(a), add(i, T, 0, a), add(S, i + day, 0, a); in(nw), in(fast), in(fc), in(slow), in(sc); for (R int i = 1; i <= day; ++i) add(S, i, nw, INF); for (R int i = 1; i < day; ++i) add(day + i, day + i + 1, 0, INF); int bd = std::max(0, day - fast); for (R int i = 1; i <= bd; ++i) add(day + i, i + fast, fc, INF); bd = std::max(0, day - slow); for (R int i = 1; i <= bd; ++i) add(day + i, i + slow, sc, INF); printf("%lld", EK()); }