#POJ 2516 Minimum Cost (多重最小费用最大流)

Description

Dearboy, a goods victualer, now comes to a big problem, and he needs your help. In his sale area there are N shopkeepers (marked from 1 to N) which stocks goods from him.Dearboy has M supply places (marked from 1 to M), each provides K different kinds of goods (marked from 1 to K). Once shopkeepers order goods, Dearboy should arrange which supply place provide how much amount of goods to shopkeepers to cut down the total cost of transport.

It's known that the cost to transport one unit goods for different kinds from different supply places to different shopkeepers may be different. Given each supply places' storage of K kinds of goods, N shopkeepers' order of K kinds of goods and the cost to transport goods for different kinds from different supply places to different shopkeepers, you should tell how to arrange the goods supply to minimize the total cost of transport.

Input

The input consists of multiple test cases. The first line of each test case contains three integers N, M, K (0 < N, M, K < 50), which are described above. The next N lines give the shopkeepers' orders, with each line containing K integers (there integers are belong to [0, 3]), which represents the amount of goods each shopkeeper needs. The next M lines give the supply places' storage, with each line containing K integers (there integers are also belong to [0, 3]), which represents the amount of goods stored in that supply place.

Then come K integer matrices (each with the size N * M), the integer (this integer is belong to (0, 100)) at the i-th row, j-th column in the k-th matrix represents the cost to transport one unit of k-th goods from the j-th supply place to the i-th shopkeeper.

The input is terminated with three "0"s. This test case should not be processed.

Output

For each test case, if Dearboy can satisfy all the needs of all the shopkeepers, print in one line an integer, which is the minimum cost; otherwise just output "-1".

Sample Input

1 3 3   
1 1 1
0 1 1
1 2 2
1 0 1
1 2 3
1 1 1
2 1 1

1 1 1
3
2
20

0 0 0

Sample Output

4
-1

 题目大意 : 输入三个数, N, M, K, 分别表示商家的数量, 原料产地的数量, 物品的总数, 以下N行, 每行K个数, 表示每个商家所需要的各个原料的数目, 接下来M行, 每行K个数, 表示每个原料产地能够产出的各个原料物品的数量, 再接下来,有K个 N * M的矩阵, 第i行 第j列表示第j个原料产地到第i个商家的路程花费, 如果输入能够满足所有商家的需求, 输出最小路程花费, 否则输出-1

思路 : 最开始我是看到了矩阵输入的方式的, 但是没有直接尝试去分层求, 而是直接把每个商家拆成了K个点, 然后建了一个多小时也没完整地把图建好T_T, 看了别人的思路说要把K个物品分开建再求和, 第一次写这种题虽然看到了思路但是没有条件反射地去运用啊。这张图硬着来建最麻烦的就是那个N 和 K, 如果我们假设每个商家只要一个物品, 每个原料产地也只产出一个物品, 图就好建多了, 点数也少了很多, 设源点为0,M个原料产地为 1 ~ M, N个商家为 M + 1 ~ M + N, 汇点为 M + N + 1。 源点到各个原料产地之间有一条边, 流量为该产地可以生产的物品的数量, 花费为0; 各个原料产地到各个商家之间有一条边, 流量为 INF, 花费为输入的花费; 各个商家到汇点有一条边, 流量为各个商家所需的物品数量, 花费为0; 这样以来,从第一个物品遍历到K个物品, 跑K次费用流, 最后判断是否等于所有商家所需就OK了。为什么说需要分层做呢, 因为他输入的时候,每一行正好就对应了该产地到商家的花费, 题目也就在告诉你这样写了 ^_^  要想时间更快的话, 每个物品建完图后都特判一次, 如果一个物品无法满足, 那么结果一定是输出-1的, 最后要注意数据范围, 做图论题一定要考虑边的大小, 边的数目需要根据题目来定, 一般的话边的数目是点的两倍, 但是对于网络流来说, 边要尽量开大一些, 因为网络流考的就是构图。

发的AC代码是没有时间优化的, 但也能过

Accepted code

#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
#include<cstdio>
using namespace std;

#define sc scanf
#define ls rt << 1
#define rs ls | 1
#define Min(x, y) x = min(x, y)
#define Max(x, y) x = max(x, y)
#define ALL(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define MEM(x, b) memset(x, b, sizeof(x))
#define lowbit(x) ((x) & (-x))
#define P2(x) ((x) * (x))

typedef long long ll;
const int MOD = 1e9 + 7;
const int MAXN = 300 + 10;
const int MAXM = 1e5 + 10;
const int INF = 0x3f3f3f3f;
inline ll qpow(ll a, ll b){ ll r = 1, t = a; while (b){ if (b & 1)r = (r*t) % MOD; b >>= 1; t = (t*t) % MOD; }return r; }

struct Edge
{
	int v, cap, w, flow, next;
}e[MAXM];
int head[MAXN], k, cnt;
int pre[MAXN], dis[MAXN], val, sp, tp;
int p[MAXN][MAXN], t[MAXN][MAXN];
bool vis[MAXN];
int n, m, ans;
void init(){
	cnt = 0;
	MEM(e, 0); MEM(head, -1);
}
void add(int u, int v, int fi, int wi){
	e[cnt].v = v; e[cnt].cap = fi; e[cnt].w = wi;
	e[cnt].next = head[u]; head[u] = cnt++;

	e[cnt].v = u; e[cnt].cap = 0; e[cnt].w = -wi;
	e[cnt].next = head[v]; head[v] = cnt++;
}
bool spfa(int s, int t){
	queue<int> q;
	for (int i = 0; i <= t; i++){
		dis[i] = INF;
		vis[i] = false;
		pre[i] = -1;
	}
	dis[s] = 0; vis[s] = true;
	q.push(s);
	while (!q.empty()){
		int now = q.front();
		q.pop();
		vis[now] = false;
		for (int i = head[now]; i != -1; i = e[i].next){
			int v = e[i].v;
			if (e[i].cap > e[i].flow && dis[v] > dis[now] + e[i].w){
				dis[v] = dis[now] + e[i].w;
				pre[v] = i;
				if (!vis[v]){
					vis[v] = true;
					q.push(v);
				}
			}
		}
	}
	if (pre[t] == -1)return false;
	return true;
}
int Mincostflow(int s, int t, int &cost){
	int flow = 0;
	cost = 0;
	while (spfa(s, t)){
		int min = INF;
		for (int i = pre[t]; i != -1; i = pre[e[i ^ 1].v]){
			if (min > e[i].cap - e[i].flow)
				min = e[i].cap - e[i].flow;
		}
		for (int i = pre[t]; i != -1; i = pre[e[i ^ 1].v]){
			e[i].flow += min;
			e[i ^ 1].flow -= min;
			cost += e[i].w * min;
		}
		flow += min;
	}
	return flow;
}

int main()
{
	while (~sc("%d %d %d", &n, &m, &k) && n + m + k) {
		MEM(p, 0); MEM(t, 0);
		sp = 0, tp = m + n + 1; val = 0;
		for (int i = m + 1; i <= m + n; i++) {
			for (int j = 1; j <= k; j++) {
				sc("%d", &p[i][j]); 
				val += p[i][j]; // 所有商家所需之和
			}
		}
		for (int i = 1; i <= m; i++) {
			for (int j = 1; j <= k; j++)
				sc("%d", &t[i][j]);  // 二维数组记录产地产出各个物品数量
		}
		int flow, ans = 0, res = 0, scnt = 0;
		for (int i = 1; i <= k; i++) {
			init();  // 每次都要初始化
			for (int x = m + 1; x <= m + n; x++) {
				add(x, tp, p[x][i], 0);  // 商家到汇点
				for (int y = 1; y <= m; y++) {
					int temp;
					sc("%d", &temp); 
					add(y, x, INF, temp); // 产地到商家
				}
			}
			for (int j = 1; j <= m; j++)
				add(0, j, t[j][i], 0); // 源点到产地
			int tot = Mincostflow(sp, tp, ans); // 加上每次的流量
			scnt += tot; res += ans;  // res为花费
		}
		if (scnt == val) cout << res << endl;
		else cout << -1 << endl;
	}
	return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值