2023.1.13【图论】【网络流24题】餐巾计划问题 LuoguP1251

2023.1.13

今日完成的[餐巾计划问题],是一道最小费用最大流的模板题,本人太弱在第一次使用dinic + spfa 完成此题时,也出现了许多问题,在此总结和提醒。

大致题意

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

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

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

思路整理

需要餐巾尽量多的情况下费用尽量少,考虑向网络流转化。考虑把每一天视为一个点即可以完成转移。脏餐巾可以向m/n天后转移,干净的餐巾会在使用的这一天转移为脏餐巾。

通过上文我们可以发现,对于每一天的餐巾,我们可以把它分为干净和脏,两个状态,所以一天就被拆成了两个点,转移如下:

​ 1.一天的脏餐巾流向n天后的干净餐巾,容量inf,边权p(n + i <= N)

​ 2.一天的脏餐巾流向m天后的干净餐巾,容量inf,边权s(m + i <= N)

​ 3.一天的脏餐巾流向明天的脏餐巾,容量inf,边权0(i + 1 <= N)

那么问题来了,我们怎么体现出“使用餐巾”、“购买餐巾”的过程呢?

时刻不要忘记网络流中源点和汇点的作用

​ 4.每一天源点流向脏餐巾,容量\(r_i\),边权0,干净餐巾流向汇点,容量\(r_i\),边权为0

​ 5.每一天源点流向干净餐巾,容量inf,边权p

原理:因为所跑是最大流,所以会尽量增多餐巾;而每天向汇点的流又是受限制的,就会刚好流\(r_i\)块餐巾;每次找的又是最短增广路,所以做法正确性得以证明,由于网络流算法时间较不稳定,理论时间复杂度是O($n ^ 2 $ m)

Code

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 5,inf = 0x7fffffff;
struct Edge{
	int v,w,c,next;
}e[N * 2];
int n,d[N],vis[N],a[N],head[N],p,m,f,q,s,S,T,ans = 0,tot = 1;//1~n干净、n + 1~2n脏 
inline void add(int x,int y,int c,int z)
{
	++tot;
	e[tot].v = y;
	e[tot].w = z;
	e[tot].c = c;
	e[tot].next = head[x];
	head[x] = tot;
}
inline bool spfa()
{
	memset(vis,0,sizeof(vis));
	for(int i=0;i<=T;i++) d[i] = inf / 2;
	queue<int> q;
	q.push(S);
	vis[S] = 1;
	d[S] = 0;
	while(!q.empty())
	{
		int now = q.front();
		q.pop();
		vis[now] = 0;
		for(int i = head[now];i;i = e[i].next)
		{
			int to = e[i].v;
			if(e[i].c <= 0) continue;
			if(d[to] > d[now] + e[i].w)
			{
				d[to] = d[now] + e[i].w;
				if(!vis[to])
				{
					vis[to] = 1;
					q.push(to);
				}
			}
		}
	}
	if(d[T] < inf / 2)
		return 1;
	return 0;
}
inline int dinic(int x,int flow)
{
	if(x == T) return flow;
	int rest = flow;
	vis[x] = 1;
	for(int i = head[x];i && rest;i = e[i].next)
	{
		int to = e[i].v;
		if(e[i].c <= 0 || d[to] != d[x] + e[i].w || vis[to]) continue;
		int k = dinic(to,min(rest,e[i].c));
		if(!k) d[to] = inf / 2;
		e[i].c -= k;
		rest -= k;
		e[i ^ 1].c += k;
		ans += k * e[i].w;
	}
	return flow - rest;
}
signed main()
{
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	cin>>p>>m>>f>>q>>s;
	S = 0,T = 2 * n + 1;
	for(int i=1;i<=n;i++)
	{
		add(S,i + n,a[i],0);
		add(i + n,S,0,0);
		add(i,T,a[i],0);
		add(T,i,0,0);
		add(S,i,a[i],p);
		add(i,S,0,-p);
		if(i + m <= n)
		{
			add(i + n,i + m,inf,f);
			add(i + m,i + n,0,-f);
		}
		if(i + q <= n)
		{
			add(i + n,i + q,inf,s);
			add(i + q,i + n,0,-s);
		}
		if(i + n + 1 <= n * 2)
		{
			add(i + n,i + n + 1,inf,0);
			add(i + n + 1,i + n,0,0);
		}
	}
	int flow = 0;
	while(spfa()) 
	{
		memset(vis,0,sizeof(vis));
		while(flow = dinic(S,inf / 2))
		{
			memset(vis,0,sizeof(vis));
			flow = 0;
		}
	}
	cout<<ans;
	return 0;
}
posted @ 2023-01-13 19:00  The_Last_Candy  阅读(20)  评论(0编辑  收藏  举报