餐巾计划问题

link

很久都没有时间写随笔了,积攒了不少网络流。本来打算昨天晚上补一补但由于夏令营我们的假没了,所以拿个下午写点吧。

一道挺巧妙的题目。

首先搞清楚流量的含义。在这道题里肯定会把流量定义为餐巾,但问题是餐巾有脏的和干净的,那咋办。有一个不成熟的想法是说每天早上向晚上连边,晚上有时还可以往早上连边,问题是这样的话无法保证每天的餐巾够用。还是那句话,一个流量不能分身,而餐巾是可以重复使用的,也就是说简单地用流量流动来代替洗餐巾是不合法的,于是想到多来点流量。如何做到呢,考虑每天早上的餐巾从哪里来,只可能是新买的或者前几天刚洗好的,这样一来就可以考虑从源点和晚上点向早上连边,早上再向汇点连边,只要向汇点的边流满了就说明那天的餐巾是够用的。然后考虑晚上的餐巾从哪里来,很明显要么是前一天剩下的,要么是早上用过的,但假如是早上用过的又不能直接从早上那里抢流量(抢走了之后可能汇边流不满但实际上是合法的),于是只能从源点那里拿。于是问题清晰起来了,考虑从源点往晚上点连这一天用掉餐巾的边即可。然后留下餐巾这个问题会影响效率,写留给第二个晚上的最优的,留给第二个早上写了也不会错但是会超时,实际上它们是等价的(等几天再洗不就是洗了再等几天吗)。跑费用流即可。

#include<bits/stdc++.h>
//#define feyn
#define int long long
const int N=5010;
const int M=5e6;
const int maxn=1e15;
using namespace std;
inline void read(int &wh){
	wh=0;int f=1;char w=getchar();
	while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
	while(w>='0'&&w<='9'){wh=wh*10+w-'0';w=getchar();}
	wh*=f;return;
}
inline int min(int s1,int s2){
	return s1<s2?s1:s2;
}

struct edge{
	int t,v1,v2,next;
}e[M];
int head[N],esum=1;
inline void adde(int fr,int to,int v1,int v2){
	e[++esum]=(edge){to,v1,v2,head[fr]};head[fr]=esum;
}
inline void add(int fr,int to,int v1,int v2){
	adde(fr,to,v1,v2);adde(to,fr,0,-v2);
}

int m,a[N],b[N],ss,tt,cnt;

queue<int>q;
int dis[N];
int vis[N],inq[N],vist,inqt;
inline bool check(){
	memset(dis,0x3f,sizeof(dis));
	vist++,inqt++;
	dis[ss]=0;q.push(ss);
	while(!q.empty()){
		int wh=q.front();q.pop();inq[wh]=0;
		for(int i=head[wh],th;i;i=e[i].next){
			if(e[i].v1==0)continue;
			int now=dis[wh]+e[i].v2;
			if(now>=dis[th=e[i].t])continue;
			dis[th]=now;if(inq[th]!=inqt)inq[th]=inqt,q.push(th);
		}
	}
	return dis[tt]<maxn;
}
int cost;
inline int dinic(int wh,int val){
	if(wh==tt)return cost+=val*dis[wh],val;
	int used=0;vis[wh]=vist;
	for(int i=head[wh],th;i;i=e[i].next){
		if(e[i].v1==0||vis[th=e[i].t]==vist||dis[th]!=dis[wh]+e[i].v2)continue;
		vis[th]=vist;int now=dinic(th,min(val,e[i].v1));
		if(now)used+=now,val-=now,e[i].v1-=now,e[i^1].v1+=now;
		if(val==0)break;
	}
	return used;
}

signed main(){
	
	#ifdef feyn
	freopen("in.txt","r",stdin);
	#endif
	
	read(m);ss=++cnt,tt=++cnt;int s1,s2;
	for(int i=1;i<=m;i++){
		a[i]=++cnt,b[i]=++cnt;read(s1);
		add(ss,a[i],s1,0);add(b[i],tt,s1,0);
		if(i>1)add(b[i-1],b[i],maxn,0);
	}
	read(s1);
	for(int i=1;i<=m;i++)add(ss,b[i],maxn,s1);
	read(s1);read(s2);
	for(int i=1;i+s1<=m;i++)add(a[i],b[i+s1],maxn,s2);
	read(s1);read(s2);
	for(int i=1;i+s1<=m;i++)add(a[i],b[i+s1],maxn,s2);
	while(check())dinic(ss,maxn);
	printf("%lld",cost);
	
	return 0;
}
posted @ 2022-07-14 15:47  Feyn618  阅读(25)  评论(0编辑  收藏  举报