解题报告 之 POJ2135 Farm Tour
Description
To show off his farm in the best way, he walks a tour that starts at his house, potentially travels through some fields, and ends at the barn. Later, he returns (potentially through some fields) back to his house again.
He wants his tour to be as short as possible, however he doesn't want to walk on any given path more than once. Calculate the shortest tour possible. FJ is sure that some tour exists for any given farm.
Input
* Lines 2..M+1: Three space-separated integers that define a path: The starting field, the end field, and the path's length.
Output
Sample Input
4 5 1 2 1 2 3 1 3 4 1 1 3 2 2 4 2
Sample Output
6
题目大意:给你一个无向图,有n个节点编号1~n,m条边。且起点为1,终点为n。给出每条边的长度,并要求每条边只能走一次,问你从起点走到终点再从终点走回起点的最短路是多少?
分析:首先直观的感觉到,如果可以重复走那么直接上SPFA即可。但是这道题要求每天边只能走一次,那么最短路算法就无效了。所以我们该怎么办呢?建议你先参考另一篇博文,这是边不重复最短路模型,这道题似乎是该模型的最小费用流升级版。
ZOJ 2760 How Many Shortest Path
http://blog.youkuaiyun.com/maxichu/article/details/45151399
那么至此我已经假设你明白边不重复最短路了。通过最大流我们可以找到有几天边不重复的最短路,但是此题并不要求是两条最短路而是总长度最短即可。所以我们必须引入最小费用流来解题了。最小费用流的理论建议看《挑战》,但是感觉《挑战》的代码实现比较凌乱不是很适合我这种小白,于是我看懂了之后自创了一个模板(赞起来。囧)。。那么此题的其实就是最基本的一个最小费用流了。
首先超级源点连接节点1,负载为2,费用为0。然后根据输入建图即可,节点n与超级汇点连接,负载为2,费用为0。然后跑一条最大流为2的最小费用流即是答案。我的模板中采用了《挑战》的h标号法,但此题并不存在负权边,我只是为了存个模板。。。
上代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<deque>
using namespace std;
const int MAXN = 90010;
const int MAXM = 910000;
const int INF = 0x3f3f3f3f;
struct Edge
{
int from, to, cap, next, cost;
};
Edge edge[MAXM];
int prevv[MAXN];
int preve[MAXN];
int dist[MAXN];
int head[MAXN];
int h[MAXN]; //标号数组
int src, des, cnt;
void addedge( int from, int to, int cap, int cost )
{
edge[cnt].from = from;
edge[cnt].to = to;
edge[cnt].cap = cap;
edge[cnt].cost = cost;
edge[cnt].next = head[from];
head[from] = cnt++;
swap( from, to );
edge[cnt].from = from;
edge[cnt].to = to;
edge[cnt].cap = 0;
edge[cnt].cost = -cost;
edge[cnt].next = head[from];
head[from] = cnt++;
}
int SPFA( )
{
deque<int> dq;
bool inqueue[MAXN];
memset( dist, INF, sizeof dist );
memset( inqueue, 0, sizeof inqueue );
dq.push_back( src );
inqueue[src] = 1;
dist[src] = 0;
while(!dq.empty())
{
int u = dq.front();
dq.pop_front();
inqueue[u] = 0;
for(int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].to;
if(edge[i].cap > 0 && dist[u] + edge[i].cost +h[u]-h[v]< dist[v])
{
dist[v] = dist[u] + edge[i].cost + h[u] - h[v];
prevv[v] = u;
preve[v] = i;
if(!inqueue[v])
{
if(!dq.empty() && dist[v] <= dist[dq.front()])
{
dq.push_front( v );
}
else
dq.push_back( v );
}
}
}
}
return 0;
}
int min_cost_flow(int f,int n)
{
memset( h, 0, sizeof h );
int cost = 0;
while(f > 0)
{
SPFA();
if(dist[des] == INF)
{
return -1;
}
for(int u = 1; u <= n; u++)
h[u] += dist[u];
h[des] += dist[des];
int d = f;
for(int i = des; i != src; i = prevv[i])
{
d = min( d, edge[preve[i]].cap );
}//此处循环比较鸡肋因为负载都为1。
f -= d;
cost += d*h[des];
for(int i = des; i != src; i = prevv[i])
{
edge[preve[i]].cap -= d;
edge[preve[i] ^ 1].cap += d;
}
}
return cost;
}
int main( )
{
int n, m;
src = 0;
des = 90005;
while(cin >> n >> m)
{
memset( head, -1, sizeof head );
cnt = 0;
int a, b, c;
for(int i = 1; i <= m; i++)
{
cin >> a >> b >> c;
addedge( a, b, 1, c );
addedge( b, a, 1, c );
}
addedge( src, 1, 2, 0 );
addedge( n, des, 2, 0 );
cout << min_cost_flow(2,n) << endl;
}
return 0;
}

本文针对POJ2135FarmTour问题,介绍了一种利用最小费用流算法解决路径问题的方法。具体地,文章通过构建一个带权无向图,采用最大流最小费用流算法寻找起点到终点再返回起点的最短路径,确保每条边仅被使用一次。
1253

被折叠的 条评论
为什么被折叠?



