网络流24题 餐巾计划问题

题目描述

一个餐厅在相继的 n nn 天里,每天需用的餐巾数不尽相同。假设第 i ii 天需要 ri r_iri 块餐巾。

餐厅可以购买新的餐巾,每块餐巾的费用为 P PP 分;或者把旧餐巾送到快洗部,洗一块需 M MM 天,

其费用为 F FF 分;或者送到慢洗部,洗一块需 N NN 天,其费用为 S SS 分(S<F S < FS<F)。

每天结束时,餐厅必须决定将多少块脏的餐巾送到快洗部,多少块餐巾送到慢洗部,

以及多少块保存起来延期送洗。但是每天洗好的餐巾和购买的新餐巾数之和,要满足当天的需求量。

试设计一个算法为餐厅合理地安排好 n nn 天中餐巾使用计划,使总的花费最小。

输入格式

第 1 11 行有 6 66 个正整数 n nnP PPM MMF FFN NNS SS

n nn 是要安排餐巾使用计划的天数,P PP 是每块新餐巾的费用,M MM 是快洗部洗一块餐巾需用天数,F F

F 是快洗部洗一块餐巾需要的费用,N NN 是慢洗部洗一块餐巾需用天数,S SS 是慢洗部洗一块餐巾需要的费用。

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

输出格式

输出餐厅在相继的 n nn 天里使用餐巾的最小总花费。

样例
样例输入
3 10 2 3 3 2
5
6
7
样例输出
145




按照题目描述:每个点拆成两个,会有两个集合 a和b。
1 每天需要x[i]条毛巾,s->ai容量为x[i]费用为0,bi->t容量为x[i]费用为0。
2 毛巾留给下一天 ai->ai+1 容量为INF费用为0.
3 毛巾送去快洗部 ai->bi+f1 容量为INF费用为f2.
4 毛巾送去慢洗部 ai->bi+p2 容量为INF费用位p2
跑最小费用最大流。
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<queue>
using namespace std;
const int maxm = 10005;
const int maxn = 1000005;
const int INF = 1e9 + 7;
struct node
{
	int u, v, flow, cost, next;
}edge[maxn];
int dis[maxm], pre[maxm], head[maxm];
int n, m, s, t, cnt, val;
void init()
{
	cnt = 0, s = 0, t = n * 2 + 1;
	memset(head, -1, sizeof(head));
}
void add(int u, int v, int w, int cost)
{
	edge[cnt].u = u, edge[cnt].v = v;
	edge[cnt].flow = w, edge[cnt].cost = cost;
	edge[cnt].next = head[u], head[u] = cnt++;
	edge[cnt].u = v, edge[cnt].v = u;
	edge[cnt].flow = 0, edge[cnt].cost = -cost;
	edge[cnt].next = head[v], head[v] = cnt++;
}
int bfs()
{
	queue<int>q;
	for (int i = s;i <= t;i++) dis[i] = INF;
	memset(pre, -1, sizeof(pre));
	dis[s] = 0;q.push(s);
	while (!q.empty())
	{
		int u = q.front();q.pop();
		//printf("%d\n", u);
		for (int i = head[u];i != -1;i = edge[i].next)
		{
			int v = edge[i].v;
			//printf("%d\n", v);
			if (dis[v] > dis[u] + edge[i].cost && edge[i].flow)
			{
				dis[v] = dis[u] + edge[i].cost;
				pre[v] = i;
				q.push(v);
			}
		}
	}
	if (dis[t] == INF) return 0;
	//printf("%d\n", dis[t]);
	return 1;
}
int MCMF()
{
	int minflow, ans = 0;
	while (bfs())
	{
		minflow = INF;
		for (int i = pre[t];i != -1;i = pre[edge[i].u])
			minflow = min(minflow, edge[i].flow);
		//printf("%d\n", minflow);
		for (int i = pre[t];i != -1;i = pre[edge[i].u])
		{
			edge[i].flow -= minflow;
			edge[i ^ 1].flow += minflow;
		}
		ans += dis[t] * minflow;
	}
	return ans;
}
int main()
{
	int i, j, k, sum, a, b, c, d;
	scanf("%d%d%d%d%d%d", &n, &val, &a, &b, &c, &d);
	init();
	for (i = 1;i <= n;i++)
	{
		scanf("%d", &k);
		add(s, i, k, 0);
		add(i + n, t, k, 0);
		add(s, i + n, INF, val);
		if (i + 1 <= n) add(i, i + 1, INF, 0);
		if (i + a <= n) add(i, i + a + n, INF, b);
		if (i + c <= n) add(i, i + c + n, INF, d);
	}
	printf("%d\n", MCMF());
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值