- 共有\(n\)天,第\(i\)天需要\(a_i\)个餐盘,每天餐盘用完后会变脏,需清洗后才能再次使用。
- 你每花\(p\)的代价可以购买一个餐盘,每个餐盘用完后可以花\(d1\)天、\(c1\)的代价或是\(d2\)天、\(c2\)的代价送去清洗。
- 求支撑到这\(n\)天结束的最小代价。
- \(n\le2000\)
网络流套路建图
此题的核心在于每天需要\(a_i\)个餐盘的条件的处理,而这实际上是相当套路的。
我们把每天拆成白天和夜晚两个点,然后从第\(i\)个白天向超级汇连容量\(a_i\)、代价\(0\)的边,从超级源向第\(i\)个夜晚连容量\(a_i\)、代价\(0\)的边。由于这张图所有到超级汇的边一定能流满,而当流满的时候也就说明了第\(i\)个白天能给出\(a_i\)个干净的盘子。
剩下的建图就非常简单了,根据题目中给出的条件建四类边:
- 购买一个盘子,从超级源向第\(i\)个白天连一条容量\(INF\)、代价\(p\)的边。
- 不洗,直接从第\(i\)个夜晚向第\(i+1\)个夜晚连一条容量\(INF\)、代价\(0\)的边(因为无需花费,但不能使用)。
- 快洗,从第\(i\)个夜晚向第\(i+d1\)个白天连一条容量\(INF\)、代价\(c1\)的边。
- 慢洗,从第\(i\)个夜晚向第\(i+d2\)个白天连一条容量\(INF\)、代价\(c2\)的边。
然后跑一遍费用流就结束了。
代码:\(O(Dinic)\)
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 2000
#define INF (int)1e9
#define LL long long
using namespace std;
int n,p,d1,c1,d2,c2;
class MinCostMaxFlow
{
private:
#define PS (2*N+2)
#define ES (6*N)
#define s (2*n+1)
#define t (2*n+2)
#define E(x) ((((x)-1)^1)+1)
int ee,lnk[PS+5];struct edge {int to,nxt,F,C;}e[2*ES+5];
int lst[PS+5],IQ[PS+5],F[PS+5];LL C[PS+5];queue<int> q;
I bool SPFA()
{
RI i,k;for(i=1;i<=t;++i) F[i]=INF,C[i]=1e18;q.push(s),C[s]=0;
W(!q.empty()) for(i=lnk[k=q.front()],q.pop(),IQ[k]=0;i;i=e[i].nxt)
{
if(!e[i].F||C[k]+e[i].C>=C[e[i].to]) continue;
C[e[i].to]=C[k]+e[lst[e[i].to]=i].C,F[e[i].to]=min(F[k],e[i].F),
!IQ[e[i].to]&&(q.push(e[i].to),IQ[e[i].to]=1);
}return F[t]^INF;
}
public:
I void Add(CI x,CI y,CI f,CI c)
{
e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y,e[ee].F=f,e[ee].C=c,
e[++ee].nxt=lnk[y],e[lnk[y]=ee].to=x,e[ee].F=0,e[ee].C=-c;
}
I LL MCMF()
{
RI x;LL g=0;W(SPFA()) {g+=C[t]*F[t],x=t;
W(x^s) e[lst[x]].F-=F[t],e[E(lst[x])].F+=F[t],x=e[E(lst[x])].to;}
return g;
}
}D;
int main()
{
RI i,x;for(scanf("%d",&n),i=1;i<=n;++i) scanf("%d",&x),D.Add(i,t,x,0),D.Add(s,n+i,x,0);//保证每天有a[i]个干净盘子
scanf("%d%d%d%d%d",&p,&d1,&c1,&d2,&c2);
for(i=1;i<=n;++i) D.Add(s,i,INF,p),i^n&&(D.Add(n+i,n+i+1,INF,0),0),//购买;不洗
i+d1<=n&&(D.Add(n+i,i+d1,INF,c1),0),i+d2<=n&&(D.Add(n+i,i+d2,INF,c2),0);//快洗;慢洗
return printf("%lld\n",D.MCMF()),0;
}