2192. 运输问题(网络流,费用流)

文章讲述了如何解决一个关于仓库和零售商店之间的货物运输问题,通过构建流网络并应用最小费用最大流算法(如EK算法和SPFA)来计算最优和最差运输方案的成本。重点在于整数型最小费用最大流的求解过程和转换策略。
摘要由CSDN通过智能技术生成

活动 - AcWing

W 公司有 m 个仓库和 n 个零售商店。

第 i 个仓库有 ai 个单位的货物;第 j 个零售商店需要 bj 个单位的货物。

货物供需平衡,即∑(i=1~m)ai=∑(j=1~n)bj。

从第 i 个仓库运送每单位货物到第 j 个零售商店的费用为 cij。

试设计一个将仓库中所有货物运送到零售商店的运输方案。

对于给定的 m 个仓库和 n 个零售商店间运送货物的费用,计算最优运输方案和最差运输方案。

输入格式

第 1 行有 2 个正整数 m 和 n,分别表示仓库数和零售商店数。

接下来的一行中有 m 个正整数 ai,表示第 i 个仓库有 ai 个单位的货物。

再接下来的一行中有 n 个正整数 bj,表示第 j 个零售商店需要 bj 个单位的货物。

接下来的 m 行,每行有 n 个整数,表示从第 i 个仓库运送每单位货物到第 j 个零售商店的费用 cij。

输出格式

第一行输出最少运输费用。

第二行输出最多运输费用。

数据范围

1≤m≤100,
1≤n≤50,
1≤ai≤30000,
1≤bi≤60000,
1≤cij≤1000

输入样例:
2 3
220 280
170 120 210
77 39 105
150 186 122
输出样例:
48500
69140

解析: 

每个仓库都可以向每个零售店运货,因此从每个仓库向每个零售店连一条边,由于运送货物的数量没有限制,所以容量都是 +∞,费用就是题目给定的值,假设从第 i 个仓库向第 j 个零售店运送货物,费用就是 cij很直观的就可以发现,我们构建的流网络中任意一个最大可行流和原问题的任意一个运输方案都是一一对应的;由于仓库到商店的边容量都是 +∞,所以其满足容量限制,同时,题目又保证 ∑(i=1~m)ai=∑(j=1~n)bj,图中的节点满足流量守恒,因此原问题的方案中就等价于每对仓库和零售店之间运输的货物数量乘以单价,因此在费用上也是一一对应的(流量守恒,容量限制)

综上所述,我们要想求原问题的运输方案的最小花费,只需要求我们构建的流网络的最小费用最大流。

本题还要求原问题的运输方案的最大花费,对应的就是流网络的最大费用最大流,一种方法是再在 EK 算法中用 spfa 求最长增广路,我们也可以将所有边的费用取反,这样原图的最大费用最大流就等价于新图的最小费用最大流,然后再将求出的费用取反回来就是运输方案的最大花费。

这里有一个小细节,spfa 不能处理负权回路,有负权并没有关系,本题建图不可能存在负权回路,并且残量网络中虽然有反向边,但是走过去再走回来等于没走,因此 spfa 是可以正常运行的

注意: 本题所有货物运输的数量肯定是整数,因此我们求的其实是整数型最小费用最大流,而 EK 算法其实求的是所有流的最小费用最大流,但是由于算法过程中我们都是用的整数类型变量,因此保证我们最终求出来的一定是整数型的最小费用最大流。

#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<math.h>
#include<map>
#include<sstream>
#include<deque>
#include<unordered_map>
#include<unordered_set>
#include<bitset>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
const int N = 150 + 10, M = (N+100*50) * 2 + 10, INF = 0x3f3f3f3f;
int m, n, S, T;
int h[N], e[M], f[M], w[M], ne[M], idx;
int q[N], d[N], pre[N],incf[N];
bool st[N];

void add(int a, int b, int c,int d) {
	e[idx] = b, f[idx] = c, w[idx] = d, ne[idx] = h[a], h[a] = idx++;
	e[idx] = a, f[idx] = 0, w[idx] = -d, ne[idx] = h[b], h[b] = idx++;
}

bool spfa() {
	int hh = 0, tt = 1;
	memset(d, 0x3f, sizeof d);
	memset(incf, 0, sizeof incf);
	q[0] = S, d[S] = 0, incf[S]=INF;
	while (hh != tt) {
		int t = q[hh++];
		if (hh == N)hh = 0;
		st[t] = 0;
		for (int i = h[t]; i != -1; i = ne[i]) {
			int j = e[i];
			if (f[i] && d[j] > d[t] + w[i]) {
				d[j] = d[t] + w[i];
				incf[j] = min(incf[t], f[i]);
				pre[j] = i;
				if (!st[j]) {
					q[tt++] = j;
					if (tt == N)tt = 0;
					st[j] = 1;
				}
			}
		}
	}
	return incf[T] > 0;
}

int EK() {
	int cost = 0;
	while (spfa()) {
		int t = incf[T];
		cost += t*d[T];
		for (int i = T; i != S; i = e[pre[i] ^ 1]) {
			f[pre[i]] -= t;
			f[pre[i] ^ 1] += t;
		}
	}
	return cost;
}

int main() {
	cin >> m >> n;
	memset(h, -1, sizeof h);
	S = 0, T = n + m + 1;
	for (int i = 1,a; i <= m; i++) {
		scanf("%d", &a);
		add(S, i, a,0);
	}
	for (int i = 1, a; i <= n; i++) {
		scanf("%d", &a);
		add(i + m, T, a, 0);
	}
	for (int i = 1; i <= m; i++) {
		for (int j = 1,a; j <= n; j++) {
			scanf("%d", &a);
			add(i, j + m, INF, a);
		}
	}
	printf("%d\n", EK());
	for (int i = 0; i < idx; i += 2) {
		f[i] += f[i ^ 1];
		f[i ^ 1] = 0;
		w[i] = -w[i];
		w[i ^ 1] = -w[i ^ 1];
	}
	printf("%d\n", -EK());
	return 0;
}

  • 20
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值