P1251 餐巾计划问题

题面

一道特别棒的费用流好题,思路非常巧妙

对于每一天,我们分为早上和晚上两个节点

每天早上会消耗\(r_i\)条干净的毛巾,可以视为流向汇点,所以我们向汇点连一条流量为\(r_i\),费用为\(0​\)的边

每天晚上会产生\(r_i\)条脏毛巾,可以视为从源点流出,所以我们从源点连一条流量为\(r_i\),费用为\(0\)的边

然后我们考虑以下四种情况:买毛巾,慢洗,快洗,留着攒味

首先是买毛巾,我们直接让毛巾从源点流向每个早上,所以从源点连一条流量无限大,费用为\(p\)的边

然后是慢洗,我们从每个晚上向\(n\)天之后的早上连一条流量无限大,费用为\(s\)的边,快洗处理方式类似

最后是保存,我们直接从每个晚上向下一个晚上连一条流量无限大,费用为\(0\)的边即可

以上所建的所有边都需要建对应的反边,这也是费用流的常规写法

图建好之后,这道题可以说已经做完了,写模板就是了

下面放代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cctype>
#include<queue>
#define ll long long
#define gc getchar
#define maxn 4005
#define maxm 100005
using namespace std;

inline ll read(){
	ll a=0;int f=0;char p=gc();
	while(!isdigit(p)){f|=p=='-';p=gc();}
	while(isdigit(p)){a=(a<<3)+(a<<1)+(p^48);p=gc();}
	return f?-a:a;
}int n,S,T,a,b,c,g,f;ll ans;

struct ahaha{
	int f,w,to,next;
}e[maxm<<1];int tot,head[maxn];
inline void add(int u,int v,int w,int f){
	e[tot]={f,w,v,head[u]};head[u]=tot++;
}

deque<int>q;
int bi[maxn],d[maxn],fl[maxn],la[maxn];
int spfa(){
	memset(fl,63,sizeof fl);memset(d,63,sizeof d);
	memset(bi,0,sizeof bi);d[S]=0;la[T]=-1;q.push_back(S);
	while(!q.empty()){
		int u=q.front();q.pop_front();bi[u]=0;
		for(int i=head[u];~i;i=e[i].next){
			int v=e[i].to;
			if(d[v]<=d[u]+e[i].f||e[i].w<=0)continue;
			d[v]=d[u]+e[i].f;la[v]=i;
			fl[v]=min(fl[u],e[i].w);
			if(bi[v])continue;bi[v]=1;
			if(q.empty()||d[v]<d[q.front()])q.push_front(v);
			else q.push_back(v);
		}
	}return ~la[T];
}

int main(){memset(head,-1,sizeof head);
	n=read();S=0;T=n<<1|1;
	for(int i=1;i<=n;++i){
		int w=read();
		add(S,i+n,w,0);add(i+n,S,0,0);
		add(i,T,w,0);add(T,i,0,0);
	}
	for(int i=1;i<n;++i){
		add(i+n,i+n+1,2147483647,0);
		add(i+n+1,i+n,0,0);
	}
	a=read();b=read();c=read();g=read();f=read();
	for(int i=1;i<=n;++i){
		add(S,i,2147483647,a);
		add(i,S,0,-a);
	}
	for(int i=n-b;i;--i){
		add(i+n,i+b,2147483647,c);
		add(i+b,i+n,0,-c);
	}
	for(int i=n-g;i;--i){
		add(i+n,i+g,2147483647,f);
		add(i+g,i+n,0,-f);
	}
	while(spfa()){
		ans+=(ll)fl[T]*d[T];
		int now=T;
		while(now!=S){
			e[la[now]].w-=fl[T];
			e[la[now]^1].w+=fl[T];
			now=e[la[now]^1].to;
		}
	}
	printf("%lld\n",ans);
	return 0;
}
posted @ 2019-02-25 21:42  子谦。  阅读(139)  评论(0编辑  收藏  举报
Live2D
//雪