Sol
先说建图:
经典构图题。将每一天拆成两个点
i,i′
,加如下6条边:
(s,i,ri,p)
——在第
i
天可以买至多
(s,i′,ri,0)
——第
i
天用剩的
(i′,i+m,∞,f)
——第
i
天的旧餐巾送到快洗部,每块
(i′,i+n,∞,s)
——第
i
天的旧餐巾送到慢洗部,每块
(i′,i′+1,∞,0)
——第
i
天的旧餐巾可以留到第
求一次最小费用流即为结果。
思路是把每个点拆开
一个代表获得当天所需的餐巾需要购买新的餐巾所造成的花费
另一个代表处理当天用掉的餐巾造成的花费
两个加起来就是当天的花费
流量限制每天的餐巾纸不大于
ri
然后跑最小费用最大流
而最大流一定可以保证满足每天的餐巾纸数量
即可以在满足每一天的餐巾纸数量的情况下取得最小花费
Code
// by spli
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<queue>
#define int long long
using namespace std;
typedef pair<int,int> Pair;
const int N=2010<<1;
const int M=N*6;
const int inf=0x3f3f3f3f;
int n;
int r[N];
int pp,ff,mm,nn,ss;
struct node{
int to,nxt,fl,c;
}e[M*2];int head[N],cnt;
int pv[N],pe[N];
queue<int>q;
bool vis[N];
int dis[N];
void add(int f,int t,int fl,int c){
e[cnt]=(node){t,head[f],fl,c};
head[f]=cnt++;
}
void add_edge(int f,int t,int fl,int c){
add(f,t,fl,c);
add(t,f,0,-c);
}
bool spfa(int S,int T){
memset(dis,inf,sizeof(dis));
q.push(S);
dis[S]=0,vis[S]=1;
while(!q.empty()){
int u=q.front();
q.pop();
vis[u]=0;
for(int i=head[u];i!=-1;i=e[i].nxt){
int v=e[i].to;
if(e[i].fl>0&&dis[v]>dis[u]+e[i].c){
dis[v]=dis[u]+e[i].c;
pv[v]=u,pe[v]=i;
if(!vis[v]){
vis[v]=1;
q.push(v);
}
}
}
}
return dis[T]<inf;
}
Pair mcf(int S,int T){
int flow;
int C=0,F=0;
while(spfa(S,T)){
flow=inf;
for(int i=T;i!=S;i=pv[i])
flow=min(flow,e[pe[i]].fl);
C+=flow*dis[T];
F+=flow;
for(int i=T;i!=S;i=pv[i])
e[pe[i]].fl-=flow,e[pe[i]^1].fl+=flow;
}
return Pair(F,C);
}
main(){
memset(head,-1,sizeof(head));
scanf("%lld",&n);
for(int i=1;i<=n;++i) scanf("%lld",&r[i]);
scanf("%lld%lld%lld%lld%lld",&pp,&mm,&ff,&nn,&ss);
int S=0,T=n*2+3;
for(int i=1;i<=n;++i){
add_edge(S,i,r[i],pp);
add_edge(i,T,r[i],0);
add_edge(S,i+n,r[i],0);
if(i+mm<=n) add_edge(i+n,i+mm,inf,ff);
if(i+nn<=n) add_edge(i+n,i+nn,inf,ss);
if(i+1<=n) add_edge(i+n,i+n+1,inf,0);
}
Pair ans=mcf(S,T);
cout<<ans.second;
return 0;
}