1. 程式人生 > 實用技巧 >KM演算法

KM演算法

1.樸素KM 時間複雜度:$O(n^4)$

演算法過程:

  1. 初始化可行頂標的值 (設定lx,ly的初始值)
  2. 用匈牙利演算法尋找相等子圖的完備匹配
  3. 若未找到增廣路則修改可行頂標的值
  4. 重複(2)(3)直到找到相等子圖的完備匹配為止

板子:

#pragma GCC optimize(3)
#pragma GCC optimize(2)
#include <iostream>
#include <string>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include 
<cmath> #include <queue> #include <functional> #include <vector> #include <map> #include <set> #include <stack> #include <fstream> #include <sstream> #include <unordered_map> #define FT(a, b) memset(a, b, sizeof(a)) #define FAT(a) memset(a, 0, sizeof(a)) using
namespace std; typedef long long ll; const int N = 5e2 + 10; const int M = 1e5 + 100; const int INF = 0x3f3f3f3f; const int mod = 1e9 + 7; int n, m, k, x, y, t; int g[N][N], lx[N], ly[N], visx[N], visy[N], match[N]; bool findpath(int x) { for (int i = 1; i < n + 1; ++i) { if (visy[i] || lx[x] + ly[i] != g[x][i])
continue; visy[i] = 1; if (match[i] == -1 || findpath(match[i])) { match[i] = x; return 1; } } return 0; } ll km() { ll ans = 0; for (int i = 1; i < n + 1; ++i) { while (1) { FAT(visx), FAT(visy); int delta = INF; if (findpath(i)) break; else { for (int i = 1; i < n + 1; ++i) if (visx[i]) for (int j = 1; j < n + 1; ++j) if (visy[j]) delta = min(delta, lx[i] + ly[j] - g[i][j]); for (int i = 1; i < n + 1; ++i) { if (visx[i]) lx[i] -= delta; if (visy[i]) ly[i] += delta; } } } } for (int i = 1; i < n + 1; ++i) ans += lx[i]; return ans; } void solve() { scanf("%d%d", &n, &m); FAT(lx), FAT(ly); FT(g, INF), FT(match, -1); for (int i = 0; i < m; ++i) { int a, b, c; scanf("%d%d%d", &a, &b, &c); g[a][b] = c; lx[a] = max(lx[a], c); } printf("%lld\n", km()); for (int i = 1; i < n + 1; ++i) printf("%d ", match[i]); puts(""); } int main() { #ifdef ONLINE_JUDGE #else freopen("/home/wjy/code/in.txt", "r", stdin); // freopen("/home/wjy/code/ans1.txt","w", stdout); #endif ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int T = 1; // scanf("%d", &T); // cin >> T; // time_t begin, end; // begin = clock(); while (T--) { solve(); } // end = clock(); // double ret = double(end - begin) / CLOCKS_PER_SEC; return 0; }