uva 10806 - Dijkstra, Dijkstra.(费用流)

题目链接:uva 10806 - Dijkstra, Dijkstra.


题目大意:给出一个无向图,求出从1走到n,再从n走回1得最短路径,每条边只能走一次。


解题思路:无向图的费用流,建立起点s指向1,容量为2,费用为0,建立汇点n + 1,n指向汇点,容量为2,费用为0.然后对于每无向边,差分成两条有向边。然后就是费用流问题,若最大流为2,输出最小费用,否则无法到达。


以前都是用邻接图去储存关系,第一次做到无向图的费用流,不懂的怎么拆边,后来发现可以储存边的状态,这样即使两条边的起点和终点不一样,也是可以求的。


#include <stdio.h>
#include <string.h>
#include <queue>

using  namespace std;

const int MAXN = 50000;
const int INF = 1 << 30;
const int N = 105;

struct edge {
	int next, im;
	int far, son;
	int cap, flow;
	int cost;
}s[MAXN];

int n, m, tmp, h[N];

void add(int x, int y, int c, int cost, int im) {

	s[tmp].next = h[x];
	h[x] = tmp;

	s[tmp].im = tmp + im;
	s[tmp].far = x;
	s[tmp].son = y;
	s[tmp].cap = c;
	s[tmp].flow = 0;
	s[tmp].cost = cost;
	tmp++;
}

void init() {

	scanf("%d", &m);
	int a, b, c;

	tmp = 0;
	memset(h, -1, sizeof(h));

	add(0, 1, 2, 0, 1);
	add(1, 0, 0, 0, -1);
	add(n, n + 1, 2, 0, 1);
	add(n + 1, n, 0, 0, -1);

	for (int i = 0; i < m; i++) {
		scanf("%d%d%d", &a, &b, &c);
		add(a, b, 1, c, 1);
		add(b, a, 0, -c, -1);
		add(b, a, 1, c, 1);
		add(a, b, 0, -c, -1);
	}
}

void solve() {
	queue<int> que;
	int ans = 0, f = 0;
	int u, d[N], vis[N], path[MAXN];

	while (1) {

		for (int i = 0; i <= n + 1; i++) d[i] = (i == 0) ? 0 : INF;
		memset(path, -1, sizeof(path));
		memset(vis, 0, sizeof(vis));

		que.push(0);
		vis[0] = 1;

		while ( !que.empty() ) {
			u = que.front(), que.pop();
			vis[u] = 0;

			for (int i = h[u]; i != -1; i = s[i].next) {
				int v = s[i].son;
				if ( s[i].cap > s[i].flow && d[v] > d[u] + s[i].cost) {
					d[v] = d[u] + s[i].cost;
					path[v] = i;

					if (vis[v] == 0) {
						que.push(v);
						vis[v] = 1;
					}
				}
			}
		}

		if (d[n + 1] == INF) break;
		int a = INF;

		for (int i = n + 1; i; i = s[path[i]].far)
			a = min(a, s[path[i]].cap - s[path[i]].flow);
		f += a;
		ans += a * d[n + 1];

		for (int i = n + 1; i; i = s[path[i]].far) {
			s[path[i]].flow += a;
			int im = s[path[i]].im;
			s[im].flow -= a;
		}
	}

	if (f < 2)
		printf("Back to jail\n");
	else
		printf("%d\n", ans);
}

int main () {

	while (scanf("%d", &n), n) {
		init();
		solve();
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值