题解
要求从起点到终点在回到起点,可以看做起点到终点流量为2的网络流。题目所给出的边没有方向设置为两条容量为1的有向边。
限制流大小为2,最后跑最小费用最大流即可。
AC代码
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <queue>
#include <string.h>
#define fst first
#define sed second
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e3 + 10;
const int M = 1e4 + 10;
int n, m, st, ed;
bool vis[N]; //在队列的节点
int dis[N], pre[N]; //pre最短路前驱通往当前点的边
struct edge
{
int v, w, c, nxt; //w容量 c代价
edge() {}
edge(int vv, int ww, int cc, int nx) : v(vv), w(ww), c(cc), nxt(nx) {}
}e[M * 4];
int h[N], idx;
void AddEdge(int u, int v, int w, int c)
{
e[idx] = edge(v, w, c, h[u]);
h[u] = idx++; //后++编号0到idx-1 结束为-1
}
bool SPFA(int st, int ed) //每个点相对于st的层次 也就是边权为1的最短路
{
queue<int> q;
memset(dis, 0x3f, sizeof(dis));
memset(pre, -1, sizeof(pre));
dis[st] = 0;
vis[st] = 1;
q.push(st);
while (!q.empty())
{
int u = q.front(); q.pop(), vis[u] = 0;
for (int i = h[u]; ~i; i = e[i].nxt)
{
int v = e[i].v, w = e[i].w, c = e[i].c; //w流量 c代价
if (dis[v] > dis[u] + c && w) //代价更低且未满流
{
dis[v] = dis[u] + c; //更新代价
pre[v] = i; //记录边编号
if (!vis[v]) //不在队伍内则入队
q.push(v), vis[v] = 1;
}
}
}
return pre[ed] != -1; //返回st是否和ed连通
}
void MCMF(int st, int ed, int &cost)
{
cost = 0;
int free = 2; //剩余所需流量 回路流量为2即可
while (free && SPFA(st, ed))
{
int mi = free;
for (int i = pre[ed]; ~i; i = pre[e[i ^ 1].v])
mi = min(mi, e[i].w);
for (int i = pre[ed]; ~i; i = pre[e[i ^ 1].v])
{
e[i].w -= mi, e[i ^ 1].w += mi;
cost += mi * e[i].c; //流量*当前边代价
}
free -= mi;
}
}
int main()
{
#ifdef LOCAL
freopen("C:/input.txt", "r", stdin);
#endif
memset(h, -1, sizeof(h));
cin >> n >> m;
for (int i = 0; i < m; ++i)
{
int u, v, c;
scanf("%d%d%d", &u, &v, &c);
AddEdge(u, v, 1, c);
AddEdge(v, u, 0, -c); //反向边和正向连续建立!!
AddEdge(v, u, 1, c); //无向图双向边 反向0容量负权
AddEdge(u, v, 0, -c);
}
int cost = 0;
MCMF(1, n, cost); //1到n再回来
printf("%d\n", cost);
return 0;
}