因为每个点只能经过一次 所以考虑拆点
这题有坑,有重边。。
KM算法
把一个点拆成入点和出点 入点在X部,出点在Y步。
如果u,v之间有路径,就在X部的u点连接Y部的v点
求完美匹配。
当完美匹配的时候,每个点都有一个入度和一个出度,可知成环。
因为完美匹配求得是最大匹配
记得把每条边权值取相反数
#include <iostream> #include <cstring> #include <cstdio> using namespace std; const int MAXN = 205; const int INF = 0x3f3f3f3f; int G[MAXN][MAXN]; int vx[MAXN], vy[MAXN]; bool visx[MAXN], visy[MAXN]; int match[MAXN]; int slack[MAXN]; int N, M; bool dfs(int x) { visx[x] = true; for (int y = 0; y < N; ++y) { if (visy[y]) continue; int gap = vx[x] + vy[y] - G[x][y]; if (gap == 0) { visy[y] = true; if (match[y] == -1 || dfs( match[y] )) { match[y] = x; return true; } } else { slack[y] = min(slack[y], gap); } } return false; } int KM() { memset(match, -1, sizeof match); memset(vy, 0, sizeof vy); for (int i = 0; i < N; ++i) { vx[i] = G[i][0]; for (int j = 1; j < N; ++j) { vx[i] = max(vx[i], G[i][j]); } } for (int i = 0; i < N; ++i) { fill(slack, slack + N, INF); while (1) { memset(visx, false, sizeof visx); memset(visy, false, sizeof visy); if (dfs(i)) break; int d = INF; for (int j = 0; j < N; ++j) if (!visy[j]) d = min(d, slack[j]); for (int j = 0; j < N; ++j) { if (visx[j]) vx[j] -= d; if (visy[j]) vy[j] += d; else slack[j] -= d; } } } int res = 0; for (int i = 0; i < N; ++i) res += G[ match[i] ][i]; return res; } int Scan() { int res = 0, flag = 0; char ch; if((ch = getchar()) == '-') flag = 1; else if(ch >= '0' && ch <= '9') res = ch - '0'; while((ch = getchar()) >= '0' && ch <= '9') res = res * 10 + (ch - '0'); return flag ? -res : res; } int main() { int T = Scan(); while (T--) { N = Scan(), M = Scan(); for (int i = 0; i <= N; ++i) { for (int j = 0; j <= N; ++j) { G[i][j] = -INF; } } int u, v, w; while (M--) { u = Scan()-1, v = Scan()-1, w = Scan(); G[u][v] = max(G[u][v], -w); } printf("%d\n", -KM()); } return 0; }