题目描述
一个餐厅在相继的 (N) 天里,每天需用的餐巾数不尽相同。假设第 (i) 天需要 (r_i)块餐巾((i=1,2,...,N))。餐厅可以购买新的餐巾,每块餐巾的费用为 (p) 分;或者把旧餐巾送到快洗部,洗一块需 m 天,其费用为 f 分;或者送到慢洗部,洗一块需 (n) 天((n>m)),其费用为 (s) 分((s<f))。
每天结束时,餐厅必须决定将多少块脏的餐巾送到快洗部,多少块餐巾送到慢洗部,以及多少块保存起来延期送洗。但是每天洗好的餐巾和购买的新餐巾数之和,要满足当天的需求量。
试设计一个算法为餐厅合理地安排好 (N) 天中餐巾使用计划,使总的花费最小。编程找出一个最佳餐巾使用计划。
输入格式
由标准输入提供输入数据。文件第 1 行有 1 个正整数 (N),代表要安排餐巾使用计划的天数。
接下来的 (N) 行是餐厅在相继的 (N) 天里,每天需用的餐巾数。
最后一行包含5个正整数(p,m,f,n,s)。(p) 是每块新餐巾的费用; (m) 是快洗部洗一块餐巾需用天数; (f) 是快洗部洗一块餐巾需要的费用; (n) 是慢洗部洗一块餐巾需用天数; (s) 是慢洗部洗一块餐巾需要的费用。
输出格式
将餐厅在相继的 (N) 天里使用餐巾的最小总花费输出
参考题解
1.从原点向每一天晚上连一条流量为当天所用餐巾x,费用为0的边,表示每天晚上从起点获得x条脏餐巾。
2.从每一天早上向汇点连一条流量为当天所用餐巾x,费用为0的边,每天白天,表示向汇点提供x条干净的餐巾,流满时表示第i天的餐巾够用 。
3.从每一天晚上向第二天晚上连一条流量为INF,费用为0的边,表示每天晚上可以将脏餐巾留到第二天晚上(注意不是早上,因为脏餐巾在早上不可以使用)。
4.从每一天晚上向这一天+快洗所用天数t1的那一天早上连一条流量为INF,费用为快洗所用钱数的边,表示每天晚上可以送去快洗部,在地i+t1天早上收到餐巾 。
5.同理,从每一天晚上向这一天+慢洗所用天数t2的那一天早上连一条流量为INF,费用为慢洗所用钱数的边,表示每天晚上可以送去慢洗部,在地i+t2天早上收到餐巾 。
6.从起点向每一天早上连一条流量为INF,费用为购买餐巾所用钱数的边,表示每天早上可以购买餐巾 。 注意,以上6点需要建反向边!3~6点需要做判断(即连向的边必须<=n)
#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define int long long
inline int read(){
int f=1,c=0;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){c=10*c+ch-'0';ch=getchar();}
return f*c;
}
const int N=1e4+10,M=2e5+10,inf=0x3f3f3f3f3f3f3f3f;
int s,t;
int nxt[M],head[N],go[M],edge[M],cost[M],cur[N],tot=1;
inline void add(int u,int v,int o1,int o2){
nxt[++tot]=head[u],head[u]=tot,go[tot]=v,edge[tot]=o1,cost[tot]=o2;
nxt[++tot]=head[v],head[v]=tot,go[tot]=u,edge[tot]=0,cost[tot]=-o2;
}
int dis[N],ret;
bool vis[N];
inline bool spfa(){
memset(dis,0x3f,sizeof(dis)); dis[s]=0;
queue<int>q; q.push(s);
while(q.size()){
int u=q.front(); q.pop(); vis[u]=0;
for(int i=head[u];i;i=nxt[i]){
int v=go[i];
if(edge[i]&&dis[v]>dis[u]+cost[i]){
dis[v]=dis[u]+cost[i];
if(!vis[v])q.push(v),vis[v]=1;
}
}
}
return dis[t]!=inf;
}
int dinic(int u,int flow){
if(u==t)return flow;
int rest=flow,k;
vis[u]=1;
for(int i=head[u];i&&rest;i=nxt[i]){
int v=go[i];
if(!vis[v]&&edge[i]&&dis[v]==dis[u]+cost[i]){
k=dinic(v,min(rest,edge[i]));
if(!k)dis[v]=-1;
ret+=k*cost[i];
edge[i]-=k;
edge[i^1]+=k;
rest-=k;
}
}
vis[u]=0;
return flow-rest;
}
int P,F,S;
signed main(){
int N=read(); t=2*N+3;
for(int i=1,x;i<=N;i++){
x=read();
add(s,i,x,0);
add(i+N,t,x,0);
}
int m=read(),t1=read(),m1=read(),t2=read(),m2=read();
for(int i=1;i<=N;i++){
if(i+1<=N) add(i,i+1,inf,0);
if(i+t1<=N) add(i,i+N+t1,inf,m1);
if(i+t2<=N) add(i,i+N+t2,inf,m2);
add(s,i+N,inf,m);
}
int flow=0,maxflow=0;
while(spfa())
while(flow=dinic(s,inf))maxflow+=flow;
cout<<ret<<endl;
}