P1251 餐巾计划问题(拆点)

发布时间 2023-08-26 16:23:44作者: 傻阙的缺

最小总花费,因为\(ri<=10000000\),排除动规,考虑网络流。因为有两个变量,时间与金钱,所以考虑费用流。

思考建图:

\(1\)、因为餐巾分为干净的餐巾肮脏的餐巾,所以考虑拆点,将\(a_i\)拆成\(a_i\)\(a_{i+N}\)分别表示第\(i\)天的白天和晚上,每天早上,我们使用干净的餐巾,每天晚上,我们得到肮脏的餐巾。

\(2\)、考虑将肮脏的餐巾送到快洗部,连一条\(a_{i+N}->a_{i+m}\)费用为\(f\),流量为\(\inf\)的边

\(3\)、考虑将肮脏的餐巾送到慢洗部,连一天\(a_{i+N}->a_{i+n}\)费用为\(s\),流量为\(\inf\)的边

\(4\)、因为我们若将肮脏的餐巾送到慢洗部和快洗部,根据网络流本质上是贪心算法,所以我们要按照他的性质来进行下列造作

\(5\)、因为无论送到快洗部还是慢洗部,洗完之后根据贪心一定会使用(不然洗来干嘛),但有些时候我们洗完之后不一定要今天使用,考虑干净的餐巾可以留到下一天,连一条\(a_{i}->a_{i+1}\)费用为\(0\),流量为\(\inf\)的边

\(6\)、考虑买新的餐巾,因为所有的餐巾来自商店,所以定义源点为商店,每一天的早上可以从商店购买新毛巾,即连一条\(st->a_i\)费用为\(p\),流量为\(\inf\)的边

\(7\)、第七条要和第八条一起看,使用干净的餐巾,因为每天都要使用餐巾,所以考虑连一条\(a_i->en\)(即汇点)费用为\(0\),流量为当天使用毛巾数量的边,表示当天使用了这么多条毛巾

\(8\)、因为晚上一定会得到当天的脏毛巾,但是白天的毛巾已经到达了汇点,所以我们可以直接从源点连一条\(st->a_{i+N}\)费用为\(0\),流量为当天使用毛巾数量的边,这些脏毛巾不用计算在第\(i\)天晚上前花费的钱,因为花费的钱在第\(i\)天早上就已经计算过了

\(9\)、优化一下,记录一下每个点的\(pre\),计算费用时用\(O(N)\)计算,其他跟费用流无异

上代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=4e5+50,INF=1e15;
ll n,a[N],m,t1,m1,m2,t2;
ll st,en,ret,s,t;
struct jgt
{
	ll to;
	ll w;
	ll cost;
	ll ne;
}e[N];
ll h[N],idx=1,pre[N];
void add(ll x,ll y,ll z,ll c)
{
	e[++idx].to=y;
	e[idx].w=z;
	e[idx].cost=c;
	e[idx].ne=h[x];
	h[x]=idx;
	e[++idx].to=x;
	e[idx].w=0;
	e[idx].cost=-c;
	e[idx].ne=h[y];
	h[y]=idx;
}
ll dis[N],cj[N],pos[N];
bool vis[N];
bool spfa()
{
	for(ll i=0;i<=2*n+1;i++) dis[i]=INF;
	queue<ll> q;
	q.push(s);
	dis[s]=0,vis[s]=1;
	cj[s]=INF;
	while(!q.empty())
	{
		ll wz=q.front();
		q.pop(),vis[wz]=0;
		for(ll i=h[wz];i;i=e[i].ne)
		{
			ll j=e[i].to;
			if(e[i].w&&dis[j]>dis[wz]+e[i].cost)
			{
				dis[j]=dis[wz]+e[i].cost;
				pre[j]=wz,pos[j]=i;
				cj[j]=min(cj[wz],e[i].w);
				if(!vis[j]) q.push(j),vis[j]=1;
			}
		}
	}
	return dis[t]!=INF;
}
int main()
{
	scanf("%lld",&n);
	for(ll i=1;i<=n;i++)
	scanf("%lld",&a[i]);
	scanf("%lld %lld %lld %lld %lld",&m,&t1,&m1,&t2,&m2);
	//i为第i天早上,i+n为第i天晚上 
	st=0,en=2*n+1;
	s=st,t=en;
	for(ll i=1;i<=n;i++)
	{
		if(i==1) add(st,i,INF,m);
		else add(i-1+n,i,INF,m);
		add(i,en,a[i],0);
		add(st,i+n,a[i],0);
		if(i+1<=n) add(i,i+1,INF,0);
		if(i+t1<=n) add(i+n,i+t1,INF,m1);
		if(i+t2<=n) add(i+n,i+t2,INF,m2);
	}
	while(spfa())
	{
		ret+=cj[t]*dis[t];
		for(ll i=en;i!=st;i=pre[i])
		{
			e[pos[i]].w-=cj[t];
			e[pos[i]^1].w+=cj[t];
		}
	}
	printf("%lld\n",ret);
	return 0;
}