把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷1251】餐巾计划问题(费用流)

点此看题面

  • 共有\(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;
}
posted @ 2021-02-25 20:26  TheLostWeak  阅读(31)  评论(0编辑  收藏  举报