题意:FJ带朋友参观自己的农场,从自己的房子出发到barn(谷仓、畜棚或车库),再从barn返回自己的房子,要求去回不走同一条路。
建图:取超级源点,并与房子连一条边,容量为2;取barn与超级汇点间的边的容量为2,中间的建图方法如代码。
因为此题是无向图,所以建边的时候如果建两条费用都是正的边的话,退流时无法修正费用。 所以应该建4条边: 第一对: a->b cost 1 b->a -cost 0 第二对: b->a cost 1 a->b -cost 0
摘了discuss里的一句话,也就是说,两个节点之间可能有多条路,来的时候走过连接这两个节点的路了,回去的时候也可以经过这两个节点,但是路却走另一条,所以要建连四条边。
这道题可作为我的邻接表实现的最小费用最大流模板。
//824K 16ms #include <iostream> using namespace std; #define min(a, b) (a<b)?a:b const int vMax = 1005; const int eMax = 40010; const int inf = 0x7fffffff; int dis[vMax], que[vMax], head[vMax], pre[vMax], qe[eMax], vis[vMax]; int ne, n, ans; struct Edge { int v, c, cost, next; } e[eMax]; bool spfa() { int i, f = 0, r = 1; memset(vis, false, sizeof(vis)); memset(pre, -1, sizeof(pre)); for(i = 0; i <= n; i++) dis[i] = inf; que[0] = 0; vis[0] = true; pre[0] = 0; dis[0] = 0; while(f != r) { int cur = que[f++]; if(f == vMax) f = 0; vis[cur] = false; for(i = head[cur]; i != -1; i = e[i].next) { int u = e[i].v; if(e[i].c && dis[u] > dis[cur] + e[i].cost) { dis[u] = dis[cur] + e[i].cost; if(!vis[u]) { vis[u] = true; que[r++] = u; if(r == vMax) r = 0; } pre[u] = cur; qe[u] = i; } } } if(pre[n] == -1) return false; else return true; } void end() { int i, tmp = inf; for(i = n; i != 0; i = pre[i]) tmp = min(tmp, e[qe[i]].c); ans += dis[n] * tmp; for(i = n; i != 0; i = pre[i]) { e[qe[i]].c -= tmp; e[qe[i]^1].c += tmp; } } void addEdge(int u, int v, int c, int cost) { e[ne].v = v; e[ne].c = c; e[ne].cost = cost; e[ne].next = head[u]; head[u] = ne++; e[ne].v = u; e[ne].c = 0; e[ne].cost = -cost; e[ne].next = head[v]; head[v] = ne++; } int main() { int N, M; int u, v, cost; //freopen("a.txt", "r", stdin); scanf("%d%d", &N, &M); memset(head, -1, sizeof(head)); ne = ans = 0; n = N+1; while(M--) { scanf("%d%d%d", &u, &v, &cost); addEdge(u, v, 1, cost); addEdge(v, u, 1, cost); } addEdge(0, 1, 2, 0); addEdge(N, n, 2, 0); while(spfa()) {end();} printf("%d\n", ans); return 0; }