题意:给定n个点(编号1~n),有m条无向边。再给出m条边的信息,起点,终点和距离。现在从1走到n,再从n走回1,没条边只能走一次,求这样走的最短路径。
将边权作为费用,流量为1(每条边只能走一次)。再新建一个源点s,s到1有一条指向1的有向边,边权为0,流量为2,再建一个汇点t,有一条从n指向t的有向边,边权为0,流量为2。建图完毕,跑费用流
最小费用:距离最短,最大流==2(来回)。
(1)距离为费用,用spfa找一条存在流量的从源点到汇点的最短路dis[t]
(2)若存在,则处理出这条上能增加的最大流量flow,然后正向边-flow,反向边+flow。
(3)ans += dis[t]*flow;
(4)重复(1)~(3)
#include
#include
#include
#include
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 1010;
const int M = 40010;
int dis[N],pre[N];
bool vis[N];
int from[M],to[M],val[M],capacity[M],nxt[M];
int head[N],tot;
int n, m;
void addedge(int u, int v, int w, int c)
{
++tot;
from[tot] = u;
to[tot] = v;
val[tot] = w;
capacity[tot] = c;
nxt[tot] = head[u];
head[u] = tot;
++tot;
from[tot] = v;
to[tot] = u;
val[tot] = -w;
capacity[tot] = 0;
nxt[tot] = head[v];
head[v] = tot;
}
bool spfa(int s, int t, int cnt)
{
memset(vis, 0, sizeof(vis));
memset(pre, -1, sizeof(pre));
for(int i = 1; i <= cnt; ++i) dis[i] = INF;
vis[s] = 1, dis[s] = 0;
queue <int> q;
q.push(s);
while(!q.empty())
{
int u = q.front();
q.pop();
vis[u] = 0;
for(int i = head[u]; ~i; i = nxt[i])
if(capacity[i])
{
int v = to[i];
if(dis[u]+val[i] < dis[v])
{
dis[v] = dis[u]+val[i];
pre[v] = i;
if(!vis[v])
{
vis[v] = 1;
q.push(v);
}
}
}
}
return dis[t] != INF;
}
int getmincost(int s, int t, int cnt)
{
int cost = 0;
while(spfa(s, t, cnt))
{
int pos = t, flow = INF;//每次增加的流量
while(pre[pos] != -1)
{
flow = min(flow, capacity[pre[pos]]);
pos = from[pre[pos]];
}
pos = t;
while(pre[pos] != -1)
{
capacity[pre[pos]] -= flow;
capacity[pre[pos]^1] += flow;
pos = from[pre[pos]];
}
cost += dis[t]*flow;
}
return cost;
}
int main()
{
while(~scanf("%d %d", &n, &m))
{
memset(head, -1, sizeof(head));
tot = -1;
for(int i = 1; i <= m; ++i)
{
int u, v, w;
scanf("%d %d %d", &u, &v, &w);
addedge(u, v, w, 1);
addedge(v, u, w, 1);
}
int s = n+1, t = n+2;
addedge(s, 1, 0, 2);
addedge(n, t, 0, 2);
printf("%d\n", getmincost(s, t, t));
}
return 0;
}