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

题目类型  最小费用最大流

题目意思
给出最多50个用户的最多50种物品的需求数量 和 最多50个供应商的相应物品的供给数量
已知对于某种物品某个用户从某个供应商处拿数量1的货的费用 问要满足所有用户所有种类物品的需求的最少费用, 不能满足输出 -1

解题方法
因为每种物品间不会造成影响 所以每种物品分开处理
建图: 新建一个源点 s 从 s 向每个用户连一条边, 边的容量为这个用户对于当前处理的物品的需求数量, 费用为0
         新建一个汇点 t, 对于每个供应商向 t 连一条边, 边的容量为这个供应商对于当前处理的物品的供给数量, 费用为0
         即除了源点 s 和汇点 t 外还有 n 个点表示用户 还有 m 个点表示供应商 那么这 n 个点与 这 m 个点之间连边, 边的容量为无限大, 费用为
         相应用户向相应供应商取1单位货的费用
那么每次用 spfa 找出一条从 s 到 t 费用最少的增广路, 并记录能增加的最大的流量, 那么最大流就增加那个流量 费用增加这条路径的费用 * 流量
最终如果最大流等于所有用户需求的物品总数表示有合法的方案 总费用加上当前解决的物品的费用

参考代码 - 有疑问的地方在下方留言 看到会尽快回复的
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>

using namespace std;

const int maxn = 50 + 10;
const int INF = 1<<29;

struct Edge {
	int from, to, cap, flow, cost;
	Edge(int _from, int _to, int _cap, int _flow, int _cost) : from(_from), to(_to), cap(_cap), flow(_flow), cost(_cost) {}
	Edge() {}
};

int sum;

struct MCMF {
	int n, m, s, t;
	vector<Edge>edges;
	vector<int>G[maxn*maxn];
	int inq[maxn*2], d[maxn*2], p[maxn*2], a[maxn*2];
	void init(int n) {
		this->n = n;
		for( int i=0; i<n; i++ ) G[i].clear();
		edges.clear();
	}
	void AddEdge(int from, int to, int cap, int cost) {
		edges.push_back(Edge(from, to, cap, 0, cost));
		edges.push_back(Edge(to, from, 0, 0, -cost));
		m = edges.size();
		G[from].push_back(m-2);
		G[to].push_back(m-1);
	}

	bool BellmanFord(int s, int t, int & flow, int & cost) {
		for( int i=0; i<n; i++ ) d[i] = INF;
		memset(inq, 0, sizeof(inq));
		d[s] = 0; inq[s] = 1; p[s] = 0; a[s] = INF;
		queue<int>Q;
		Q.push(s);
		while(!Q.empty()) {
			int u = Q.front(); Q.pop();
			inq[u] = 0;
			for( int i=0; i<G[u].size(); i++ ) {
				Edge & e = edges[G[u][i]];
				if(e.cap > e.flow && d[e.to] > d[u] + e.cost) {
					d[e.to] = d[u] + e.cost;
					p[e.to] = G[u][i];
					a[e.to] = min(a[u], e.cap - e.flow);
					if(!inq[e.to]) { Q.push(e.to); inq[e.to] = 1; }
				}
			}
		}
		if(d[t] == INF) return false;
		flow += a[t];
		cost += d[t] * a[t];
		int u = t;
		while(u != s) {
			edges[p[u]].flow += a[t];
			edges[p[u]^1].flow -= a[t];
			u = edges[p[u]].from;
		}
		return true;
	}

	int Mincost(int s, int t) {
		int flow = 0, cost = 0;
		while(BellmanFord(s, t, flow, cost));
		if(flow < sum) return -1;
		else return cost;
	}
}MC;

int need[maxn][maxn], have[maxn][maxn];

int main() {
	freopen("in", "r", stdin);
	int n, m, k;
	while(scanf("%d%d%d", &n, &m, &k), n || m || k) {
		for( int i=0; i<n; i++ ) for( int j=0; j<k; j++ ) {
			scanf("%d", &need[i][j]);
		}
		for( int i=0; i<m; i++ ) for( int j=0; j<k; j++ ) {
			scanf("%d", &have[i][j]);
		}
		int ans = 0;
		for( int h=0; h<k; h++ ) {
			MC.init(1+n+m+1);
			sum = 0;
			for( int i=0; i<n; i++ ) MC.AddEdge(0, 1+i, need[i][h], 0), sum += need[i][h];
			for( int i=0; i<m; i++ ) MC.AddEdge(1+n+i, 1+n+m, have[i][h], 0);
			for( int i=0; i<n; i++ ) for( int j=0; j<m; j++ ) {
				int tmp;
				scanf("%d", &tmp);
				MC.AddEdge(1+i, 1+n+j, INF, tmp);
			}
			if(ans == -1) continue;
			int tmp = MC.Mincost(0, 1+n+m);
			if(tmp == -1) ans = -1;
			else ans += tmp;
		}
		if(ans == -1) printf("-1\n");
		else printf("%d\n", ans);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值