前置知识
题意
题目描述
一个餐厅在相继的 N N N 天里,每天需用的餐巾数不尽相同。假设第 i i i 天需要 r i r_i ri块餐巾 ( i = 1 , 2 , . . . , N ) ( i=1,2,...,N) (i=1,2,...,N)。餐厅可以购买新的餐巾,每块餐巾的费用为 p p p 分;或者把旧餐巾送到快洗部,洗一块需 m 天,其费用为 f 分;或者送到慢洗部,洗一块需 n n n 天 ( n > m ) (n>m) (n>m),其费用为 s s s 分 ( s < f ) (s<f) (s<f)。
每天结束时,餐厅必须决定将多少块脏的餐巾送到快洗部,多少块餐巾送到慢洗部,以及多少块保存起来延期送洗。但是每天洗好的餐巾和购买的新餐巾数之和,要满足当天的需求量。
试设计一个算法为餐厅合理地安排好 N N N 天中餐巾使用计划,使总的花费最小。编程找出一个最佳餐巾使用计划。
输入输出格式
输入格式:
由标准输入提供输入数据。文件第 1 1 1 行有 1 1 1 个正整数 N N N,代表要安排餐巾使用计划的天数。
接下来的 N N N 行是餐厅在相继的 N N N 天里,每天需用的餐巾数。
最后一行包含 5 5 5个正整数 p , m , f , n , s p,m,f,n,s p,m,f,n,s。 p p p 是每块新餐巾的费用; m m m 是快洗部洗一块餐巾需用天数; f f f 是快洗部洗一块餐巾需要的费用; n n n 是慢洗部洗一块餐巾需用天数; s s s 是慢洗部洗一块餐巾需要的费用。
输出格式:
将餐厅在相继的 N N N 天里使用餐巾的最小总花费输出
总体思路
最小费用最大流。
首先,我们拆点,将一天拆成晚上和早上,每天晚上会受到脏餐巾(来源:当天早上用完的餐巾,在这道题中可理解为从原点获得),每天早上又有干净的餐巾(来源:购买、快洗店、慢洗店)。
- 从原点向每一天晚上连一条流量为当天所用餐巾 x x x,费用为 0 0 0的边,表示每天晚上从起点获得 x x x条脏餐巾。
- 从每一天早上向汇点连一条流量为当天所用餐巾 x x x,费用为 0 0 0的边,每天白天,表示向汇点提供 x x x条干净的餐巾,流满时表示第 i i i天的餐巾够用 。
- 从每一天晚上向第二天晚上连一条流量为 I N F INF INF,费用为 0 0 0的边,表示每天晚上可以将脏餐巾留到第二天晚上(注意不是早上,因为脏餐巾在早上不可以使用)。
- 从每一天晚上向这一天加快洗所用天数 t 1 t1 t1的那一天早上连一条流量为 I N F INF INF,费用为快洗所用钱数的边,表示每天晚上可以送去快洗部,在地 i + t 1 i+t1 i+t1天早上收到餐巾 。
- 同理,从每一天晚上向这一天加慢洗所用天数 t 2 t2 t2的那一天早上连一条流量为 I N F INF INF,费用为慢洗所用钱数的边,表示每天晚上可以送去慢洗部,在地 i + t 2 i+t2 i+t2天早上收到餐巾 。
- 从起点向每一天早上连一条流量为
I
N
F
INF
INF,费用为购买餐巾所用钱数的边,表示每天早上可以购买餐巾 。
注意,以上 6 6 6点需要建反向边! 3 6 3~6 3 6点需要做判断(即连向的边必须 < = n <=n <=n)
代码
#include<bits/stdc++.h>
#define int long long
#define Inf 2147483647
using namespace std;
int first[1000005],nxt[1000005],to[1000005],w[1000005],flow[1000005],tot=1;
int d[100005],incf[100005],inq[100005],pre[100005],n,c1,t2,c2,t3,c3,s,t,a[100005];
int Read(){
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') f=-1;
ch=getchar();
}
while(isdigit(ch)){
x=(x<<3)+(x<<1)+ch-'0';
ch=getchar();
}
return x*f;
}
void Write(int x){
if(x<0){
putchar('-');
x=-x;
}
if(x>9){
Write(x/10);
}
putchar(x%10+'0');
}
void Add(int x,int y,int z,int c){
nxt[++tot]=first[x];
first[x]=tot;
to[tot]=y;
flow[tot]=z;
w[tot]=c;
}
int mf,ans;
bool spfa(int st){
queue<int> q;
memset(d,127,sizeof(d));
memset(incf,127,sizeof(incf));
memset(inq,0,sizeof(inq));
q.push(st);
d[st]=0,inq[st]=1;
pre[t]=-1;
while(!q.empty()){
int u=q.front();
inq[u]=0;
q.pop();
for(int e=first[u];e;e=nxt[e]){
if(!flow[e]) continue;
int v=to[e];
if(d[v]>d[u]+w[e]){
d[v]=d[u]+w[e];
incf[v]=min(incf[u],flow[e]);
pre[v]=e;
if(!inq[v]) q.push(v);
}
}
}
return pre[t]!=-1;
}
void update(){
int x=t;
while(x!=s){
int i=pre[x];
flow[i]-=incf[t];
flow[i^1]+=incf[t];
x=to[i^1];
}
mf+=incf[t];
ans+=d[t]*incf[t];
}
signed main(){
n=Read();
for(int i=1;i<=n;i++){
a[i]=Read();
}
c1=Read(),t2=Read(),c2=Read(),t3=Read(),c3=Read();
s=0,t=2*n+1;
for(int i=1;i<=n;i++){
Add(s,i,a[i],0);
Add(i,s,0,0);
}
for(int i=1;i<=n;i++){
Add(i+n,t,a[i],0);
Add(t,i+n,0,0);
}
for(int i=1;i<n;i++){
Add(i,i+1,Inf,0);
Add(i+1,i,0,0);
}
for(int i=1;i<=n;i++){
if(i+t2<=n){
Add(i,i+t2+n,Inf,c2);
Add(i+t2+n,i,0,-c2);
}
if(i+t3<=n){
Add(i,i+t3+n,Inf,c3);
Add(i+t3+n,i,0,-c3);
}
}
for(int i=1;i<=n;i++){
Add(s,i+n,Inf,c1);
Add(i+n,s,0,-c1);
}
while(spfa(s)) update();
cout<<ans<<endl;
return 0;
}