题意:
农场有N块田(1...N),1号是FJ住的房子,N号是大型仓库,一些田之间有双向路连接,且有一个路径长度,从房子出发到达大型仓库,然后再回到房子,要求对于每条路,只能走一次,求往返的最短距离。
思路:
经典费用流问题。我们可以通过设置一条边上的流量为1来表示每条边只走一次的限制,然后每个边的花费就是该路径长度。所以最关键的是如何表示我们要求两条最短路呢,我们去看每个点被选择了多少次,只有发现起点1和终点N各被选择了两次,其它点都只选择了一次,因此问题就变成了:最大流为2的时候最小费用为多少。所以我们建立一个超级源点连接1号点,容量为2,花费为0,即可实现选择1号点两次,同理建立一个超级汇点连接n号点,容量为2,花费为0,实现选择n号点两次。其它节点容量为1,花费为路径长度。
注意:
其实只建立一个超级源点或者只建立一个超级汇点也可行,其实就是一个点选择两次,另一个点选择无限制次,所以这样也可行。另外图是无向图,所以要在加边时,原图中的一条边要变成网络流图中的两条边(如果把反向负权费用边也算上就总共4条边)。
反思:
无向图对于一条无向边需要建立四条边:u->v边及其反向边和v->u及其反向边,无向图一条边的对应边存在是不会影响反向边的作用的,也不会造成其他影响。以前一直以为反向边和当前边在无向图中的相对边是一个概念,其实不是,而且这也打消了之前的一个疑惑:存在一个已经存在的边的对应边网络流会出错误。所以以后是有向图就建一条边及其反向边,是无向图就建两条相对的边及它们的反向边即可。
收获:
利用费用流解决问题,要抓住三点:一,费用是结果,所以题目本身需要时最值关系。二,最大流是条件,是关键也是难点,需要把题目中隐藏的最值条件挖掘出来。三,利用题目的限制条件来构造网络图。(参考)
代码:
#include <algorithm>
#include <iostream>
#include <string.h>
#include <cstdio>
#include <queue>
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 1005;
const int maxm = 100020;
struct node
{
int v, c, w, next;
} edge[maxm];
int no, head[maxn];
int n, m;
int S, T;
int vis[maxn], dis[maxn];
queue<int> q;
int pre[maxn], rec[maxn];
inline void init()
{
no = 0;
memset(head, -1, sizeof head);
}
inline void add(int u, int v, int w, int c)
{
edge[no].v = v; edge[no].w = w;
edge[no].c = c; edge[no].next = head[u];
head[u] = no++;
edge[no].v = u; edge[no].w = 0;
edge[no].c = -c; edge[no].next = head[v];
head[v] = no++;
}
bool SPFA()
{
memset(dis, 0x3f, sizeof dis);
memset(vis, 0, sizeof vis);
while(!q.empty()) q.pop();
pre[S] = S; dis[S] = 0;
q.push(S); vis[S] = 1;
while(!q.empty())
{
int top = q.front(); q.pop();
vis[top] = 0;
for(int k = head[top]; k != -1; k = edge[k].next)
{
if(edge[k].w && dis[edge[k].v] > dis[top]+edge[k].c)
{
dis[edge[k].v] = dis[top]+edge[k].c;
pre[edge[k].v] = top; rec[edge[k].v] = k;
if(!vis[edge[k].v])
vis[edge[k].v] = 1, q.push(edge[k].v);
}
}
}
if(dis[T] == inf) return false;
return true;
}
pair<int, int> mincost_maxflow()
{
int mincost = 0, maxflow = 0;
while(SPFA())
{
int flow = inf;
for(int k = T; k != S; k = pre[k])
flow = min(flow, edge[rec[k]].w);
maxflow += flow;
for(int k = T; k != S; k = pre[k])
{
mincost += flow*edge[rec[k]].c;
edge[rec[k]].w -= flow;
edge[rec[k]^1].w += flow;
}
}
return make_pair(maxflow, mincost);
}
int main()
{
int u, v, c;
scanf("%d %d", &n, &m); init();
for(int i = 1; i <= m; ++i)
{
scanf("%d %d %d", &u, &v, &c);
add(u, v, 1, c);
add(v, u, 1, c);
}
S = 0, T = n+1;
add(S, 1, 2, 0);
add(n, T, 2, 0);
pair<int, int> pr = mincost_maxflow();
printf("%d\n", pr.second);
return 0;
}
继续加油~