poj2135

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


 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值