餐巾计划问题【网络流24题】

题目链接

题意:

一家餐厅第 i i i 天需要 r i r_i ri张餐巾,每天餐巾用完后就会变脏,对于餐巾可以

  1. 买一条新的,费用 p
  2. 送到快洗店,m 天后送回来,费用 f
  3. 送到满洗店,n 天后送回来,费用 s
  4. 把脏餐巾留着到以后再洗

题解:

把每一天拆成两个点——早上和晚上,早上接收新餐巾,晚上接收脏餐巾,可以这样建图

  1. 源点 S —> 第 i i i 天早上,流量 INF,费用 p(因为早上可以买无数条)
  2. i i i 天早上 —> 汇点 T,流量 r[i],费用 0(表示这一天的结束)
  3. 源点 S —> 第 i i i 天晚上,流量 r[i] ,费用 0(晚上收到脏餐巾)
  4. i i i 天晚上 —> 第 i + m i+m i+m天早上,流量 INF,费用f(快洗)
  5. i i i 天晚上 —> 第 i + n i+n i+n天早上,流量 INF,费用s(慢洗)
  6. i i i 天晚上 —> 第 i + 1 i+1 i+1天晚上,流量 INF,费用0(把脏餐巾留到下一天)

对于样例的图就是这样的
在这里插入图片描述

#include<iostream>
#include<sstream>
#include<string>
#include<queue>
#include<map>
#include<unordered_map>
#include<set>
#include<vector>
#include<stack>
#include <utility>
#include<list>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<iomanip>
#include<time.h>
#include<random>
using namespace std;
#include<ext/pb_ds/priority_queue.hpp>
#include<ext/pb_ds/tree_policy.hpp>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>
using namespace __gnu_pbds;
#include<ext/rope>
using namespace __gnu_cxx;

#define int long long
#define PI acos(-1.0)
#define eps 1e-9
#define lowbit(a) ((a)&-(a))

const int mod = 1e9+7;
int qpow(int a,int b){
	int ans=1;
	while(b){
		if(b&1)ans=(ans*a)%mod;
		a=(a*a)%mod;
		b>>=1;
	}
	return ans;
}
const int INF = 0x3f3f3f3f;
const int N = 1e6+10;
struct node{
	int to,next;
	int cap,cost;
}e[N];
int head[N],cnt=-1,S,T; 
void add(int u,int v,int cap,int cost){
	e[++cnt]={v,head[u],cap,cost}; head[u]=cnt; swap(u,v);
	e[++cnt]={v,head[u],0,-cost}; head[u]=cnt;
}
int r[2005],p,m,f,n,s,a;
int mincost,maxflow,dis[5005],flw[5005],pre[5005],vis[5005];
bool spfa(){
	memset(dis,-1,sizeof dis);
	memset(vis,0,sizeof vis);
	dis[S]=0,flw[S]=INF,vis[S]=1,pre[S]=-1;
	queue<int>q;
	q.push(S);
	while(!q.empty()){
		int u=q.front(); q.pop();
		vis[u]=0;
		for(int i=head[u];~i;i=e[i].next){
			int v=e[i].to;
			if(!e[i].cap)continue;
			if(!~dis[v]||dis[v]>dis[u]+e[i].cost){
				dis[v]=dis[u]+e[i].cost;
				flw[v]=min(flw[u],e[i].cap);
				pre[v]=i;
				if(!vis[v])q.push(v),vis[v]=1;
			}
		}
	}
	return ~dis[T];
}
void update(){
	for(int i=pre[T];~i;i=pre[e[i^1].to])
		e[i].cap-=flw[T],e[i^1].cap+=flw[T];
	maxflow+=flw[T];
	mincost+=dis[T]*flw[T];
}
#define endl '\n'
signed main(){
	std::ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	memset(head,-1,sizeof head);
	cin>>a; 
	S=2*a+1,T=2*a+2;    //源点S,汇点T
	for(int i=1;i<=a;i++)cin>>r[i];
	cin>>p>>m>>f>>n>>s;
	for(int i=1;i<=a;i++){
		add(S,i+a,r[i],0);             //情况1
		add(i,T,r[i],0);               //情况2
		add(S,i,INF,p);                //情况3
		if(i+m<=a)add(i+a,i+m,INF,f);  //情况4
		if(i+n<=a)add(i+a,i+n,INF,s);  //情况5
		if(i+1<=a)add(i+a,i+a+1,INF,0);//情况6
	}
	while(spfa())update();//费用流板子
	cout<<mincost<<endl;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值