ZOJ 3362 Beer Problem(最小费用最大流)

看了报告才写出来的.

添加一个汇点,把所有非源点城市添加一条边到汇点,容量为无穷大,代价为负的该城市的单价(巧妙的转换).

然后就是无向图的最小费用最大流问题了,最后把答案取反.

#include <iostream>
#include <queue>
#include <cstdio>
#define INF 0X20202020
using namespace std;
const int maxn = 110;
struct edge{
	int u, v, cap, cost, next;
}es[maxn * maxn];

int head[maxn], fa[maxn], idx[maxn], inq[maxn], dis[maxn], n, m;
void addEdge(int u, int v, int cap, int cost, int & eidx){
	es[eidx].u = u;
	es[eidx].v = v;
	es[eidx].cap = cap;
	es[eidx].cost = cost;
	es[eidx].next = head[u];
	head[u] = eidx ++;
	
	es[eidx].u = v;
	es[eidx].v = u;
	es[eidx].cap = 0;
	es[eidx].cost = -cost;
	es[eidx].next = head[v];
	head[v] = eidx ++;
}

void spfa(int s, int e){
	memset(dis, 0X20, sizeof(dis));
	memset(inq, 0, sizeof(inq));
	memset(fa, -1, sizeof(fa));
	memset(idx, -1, sizeof(idx));
	queue<int> q;
	q.push(s);
	inq[s] = 1;
	dis[s] = 0;
	while (q.size()){
		int u = q.front();q.pop();
		inq[u] = 0;
		for (int ne = head[u]; ne != -1; ne = es[ne].next){
			int v = es[ne].v, w = es[ne].cost;
			if (es[ne].cap && dis[v] > dis[u] + w){
				dis[v] = dis[u] + w;
				fa[v] = u;
				idx[v] = ne;
				if(!inq[v]){
					inq[v] = 1;
					q.push(v);
				}
			}
		}
	}
}
int minCostMaxFlow(int s, int e){
	int ans = 0;
	while (1){
		spfa(s, e);
		if(dis[e] >= 0)break;
		int minv = INF;
		for (int i = e; idx[i] != -1; i = fa[i]){
			int ei = idx[i];
			if(es[ei].cap < minv){
				minv = es[ei].cap;
			}
		}
		for (int i = e; idx[i] != -1; i = fa[i]){
			es[idx[i]].cap -= minv;
			es[idx[i] ^ 1].cap += minv;
		}
		ans += dis[e] * minv;
	}
	return -ans;
}
int main(){
	while (scanf("%d%d", &n, &m) == 2){
		memset(head, -1, sizeof(head));
		int eidx = 0;
		for (int i = 2; i <= n; ++i ){
			int p;
			scanf("%d", &p);
			addEdge(i, n + 1, INF, -p, eidx);
		}
		for (int i = 0; i < m; ++i){
			int u, v, w, c;
			scanf("%d %d %d %d", &u, &v, &w, &c);
			addEdge(u, v, w, c, eidx);
			addEdge(v, u, w, c, eidx);
		}
		printf("%d\n",minCostMaxFlow(1, n + 1));
		
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值