传送门
I think
最小费用最大流。增设源汇点S,T,每i天分两个点xi,yi,分别表示每天应有餐巾与最终解决的餐巾数。记
(x,y,u,v)
表示x连向y点容量为u费用为v点的边,连接以下边
(S,xi,ai,0),(yi,T,ai,0),(xi,xi+1,Inf,0),(xi,yi+qk,Inf,qc),(xi,yi+lk,Inf,lc);
最后用SPFA+增广路思想求出最小费用即可。
另算法函数名很无赖地用了SPFA。
Code
#include<cstdio>
#include<queue>
using namespace std;
const int sm = 2200;
const int sn = 12200;
const int Inf = 0x3f3f3f3f;
int N,nw,qk,qc,lw,lc,Ans,tot=1,S,T;
int to[sn],nxt[sn],hd[sm],c[sn],f[sn];
int pre[sm],vis[sm],cst[sm];
int Min(int x,int y) { return x<y?x:y; }
void Add(int u,int v,int w,int val) {
to[++tot]=v,nxt[tot]=hd[u],hd[u]=tot,c[tot]=w,f[tot]=val;
to[++tot]=u,nxt[tot]=hd[v],hd[v]=tot,c[tot]=0,f[tot]=-val;
}
void SPFA() {
int t,df; queue<int>q;
while(1) {
for(int i=1;i<=T;++i)
pre[i]=vis[i]=0,cst[i]=Inf;
cst[S]=0,vis[S]=1,q.push(S);
while(!q.empty()) {
t=q.front(),q.pop();
vis[t]=0;
for(int i=hd[t];i;i=nxt[i])
if(cst[to[i]]>cst[t]+f[i]&&c[i]>0) {
cst[to[i]]=cst[t]+f[i];
pre[to[i]]=i;
if(!vis[to[i]]) {
vis[to[i]]=1;
q.push(to[i]);
}
}
}
if(cst[T]==Inf)break;
df=Inf;
for(int i=T;i!=S;i=to[pre[i]^1])
df=Min(df,c[pre[i]]);
for(int i=T;i!=S;i=to[pre[i]^1])
c[pre[i]]-=df,c[pre[i]^1]+=df;
Ans+=df*cst[T];
}
printf("%d\n",Ans);
}
int main() {
int u;
scanf("%d%d%d%d%d%d",&N,&nw,&qk,&qc,&lw,&lc);
S=N<<1|1,T=S+1;
for(int i=1;i<=N;++i) {
scanf("%d",&u);
Add(S,i,u,0),Add(i+N,T,u,0);
Add(S,i+N,Inf,nw);
if(i+1<=N) Add(i,i+1,Inf,0);
if(i+qk<=N) Add(i,i+N+qk,Inf,qc);
if(i+lw<=N) Add(i,i+N+lw,Inf,lc);
}
SPFA();
return 0;
}