网络流24题——餐巾计划问题

本文介绍了一种利用最小费用最大流算法解决餐巾使用计划的问题,通过合理规划餐巾的购买、清洗及储存,使得餐厅在连续若干天内的总花费达到最小。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前置知识

网络流

题意

题目描述

一个餐厅在相继的 N N N 天里,每天需用的餐巾数不尽相同。假设第 i i i 天需要 r i r_i ri块餐巾 ( i = 1 , 2 , . . . , N ) ( i=1,2,...,N) (i=1,2,...,N)。餐厅可以购买新的餐巾,每块餐巾的费用为 p p p 分;或者把旧餐巾送到快洗部,洗一块需 m 天,其费用为 f 分;或者送到慢洗部,洗一块需 n n n ( n &gt; m ) (n&gt;m) (n>m),其费用为 s s s ( s &lt; f ) (s&lt;f) (s<f)

每天结束时,餐厅必须决定将多少块脏的餐巾送到快洗部,多少块餐巾送到慢洗部,以及多少块保存起来延期送洗。但是每天洗好的餐巾和购买的新餐巾数之和,要满足当天的需求量。

试设计一个算法为餐厅合理地安排好 N N N 天中餐巾使用计划,使总的花费最小。编程找出一个最佳餐巾使用计划。

输入输出格式
输入格式:

由标准输入提供输入数据。文件第 1 1 1 行有 1 1 1 个正整数 N N N,代表要安排餐巾使用计划的天数。

接下来的 N N N 行是餐厅在相继的 N N N 天里,每天需用的餐巾数。

最后一行包含 5 5 5个正整数 p , m , f , n , s p,m,f,n,s p,m,f,n,s p p p 是每块新餐巾的费用; m m m 是快洗部洗一块餐巾需用天数; f f f 是快洗部洗一块餐巾需要的费用; n n n 是慢洗部洗一块餐巾需用天数; s s s 是慢洗部洗一块餐巾需要的费用。

输出格式:

将餐厅在相继的 N N N 天里使用餐巾的最小总花费输出

总体思路

最小费用最大流。
首先,我们拆点,将一天拆成晚上和早上,每天晚上会受到脏餐巾(来源:当天早上用完的餐巾,在这道题中可理解为从原点获得),每天早上又有干净的餐巾(来源:购买、快洗店、慢洗店)。

  • 从原点向每一天晚上连一条流量为当天所用餐巾 x x x,费用为 0 0 0的边,表示每天晚上从起点获得 x x x条脏餐巾。
  • 从每一天早上向汇点连一条流量为当天所用餐巾 x x x,费用为 0 0 0的边,每天白天,表示向汇点提供 x x x条干净的餐巾,流满时表示第 i i i天的餐巾够用 。
  • 从每一天晚上向第二天晚上连一条流量为 I N F INF INF,费用为 0 0 0的边,表示每天晚上可以将脏餐巾留到第二天晚上(注意不是早上,因为脏餐巾在早上不可以使用)。
  • 从每一天晚上向这一天加快洗所用天数 t 1 t1 t1的那一天早上连一条流量为 I N F INF INF,费用为快洗所用钱数的边,表示每天晚上可以送去快洗部,在地 i + t 1 i+t1 i+t1天早上收到餐巾 。
  • 同理,从每一天晚上向这一天加慢洗所用天数 t 2 t2 t2的那一天早上连一条流量为 I N F INF INF,费用为慢洗所用钱数的边,表示每天晚上可以送去慢洗部,在地 i + t 2 i+t2 i+t2天早上收到餐巾 。
  • 从起点向每一天早上连一条流量为 I N F INF INF,费用为购买餐巾所用钱数的边,表示每天早上可以购买餐巾 。
    注意,以上 6 6 6点需要建反向边! 3   6 3~6 3 6点需要做判断(即连向的边必须 &lt; = n &lt;=n <=n

代码

#include<bits/stdc++.h>
#define int long long
#define Inf 2147483647
using namespace std;
int first[1000005],nxt[1000005],to[1000005],w[1000005],flow[1000005],tot=1;
int d[100005],incf[100005],inq[100005],pre[100005],n,c1,t2,c2,t3,c3,s,t,a[100005];
int Read(){
	int x=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-')  f=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		x=(x<<3)+(x<<1)+ch-'0';
		ch=getchar();
	}
	return x*f;
}
void Write(int x){
	if(x<0){
		putchar('-');
		x=-x;
	}
	if(x>9){
		Write(x/10);
	}
	putchar(x%10+'0');
}
void Add(int x,int y,int z,int c){
	nxt[++tot]=first[x];
	first[x]=tot;
	to[tot]=y;
	flow[tot]=z;
	w[tot]=c;
}
int mf,ans;
bool spfa(int st){
	queue<int> q;
	memset(d,127,sizeof(d));
	memset(incf,127,sizeof(incf));
	memset(inq,0,sizeof(inq));
	q.push(st);
	d[st]=0,inq[st]=1;
	pre[t]=-1;
	while(!q.empty()){
		int u=q.front();
		inq[u]=0;
		q.pop();
		for(int e=first[u];e;e=nxt[e]){
			if(!flow[e])  continue;
			int v=to[e];
			if(d[v]>d[u]+w[e]){
				d[v]=d[u]+w[e];
				incf[v]=min(incf[u],flow[e]);
				pre[v]=e;
				if(!inq[v])  q.push(v);
			}
		}
	}
	return pre[t]!=-1;
}
void update(){
	int x=t;
	while(x!=s){
		int i=pre[x];
		flow[i]-=incf[t];
		flow[i^1]+=incf[t];
		x=to[i^1];
	}
	mf+=incf[t];
	ans+=d[t]*incf[t];
}
signed main(){
	n=Read();
	for(int i=1;i<=n;i++){
		a[i]=Read();
	}
	c1=Read(),t2=Read(),c2=Read(),t3=Read(),c3=Read();
	s=0,t=2*n+1;
	for(int i=1;i<=n;i++){
		Add(s,i,a[i],0);
		Add(i,s,0,0);
	}
	for(int i=1;i<=n;i++){
		Add(i+n,t,a[i],0);
		Add(t,i+n,0,0);
	}
	for(int i=1;i<n;i++){
		Add(i,i+1,Inf,0);
		Add(i+1,i,0,0);
	}
	for(int i=1;i<=n;i++){
		if(i+t2<=n){
			Add(i,i+t2+n,Inf,c2);
			Add(i+t2+n,i,0,-c2);
		}
		if(i+t3<=n){
			Add(i,i+t3+n,Inf,c3);
			Add(i+t3+n,i,0,-c3);
		}
	}
	for(int i=1;i<=n;i++){
		Add(s,i+n,Inf,c1);
		Add(i+n,s,0,-c1);
	}
	while(spfa(s))  update();
	cout<<ans<<endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值