题意: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;
}