題解
要求從起點到終點在回到起點,可以看做起點到終點流量爲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;
}