【模板】二分图最大权完美匹配(KM算法)/洛谷P6577

1|0题目链接

https://www.luogu.com.cn/problem/P6577

2|0题目大意

给定一个二分图,其左右点的个数各为 n,带权边数为 m,保证存在完美匹配。

求一种完美匹配的方案,使得最终匹配边的边权之和最大。

3|0题目解析

二分图最大权完美匹配,一般用 KM 算法完成。

基础版的 KM 算法,是匈牙利算法的改进,依然采用 DFS 策略,速度较慢。

而改进版的 KM 算法采用 BFS 策略,有效提升速度。

点数为 n,边数为 m

时间复杂度: O(n2m)

4|0参考代码

基础版(会超时)

#include <bits/stdc++.h> using namespace std; typedef long long ll; const int INF = 0x3f3f3f3f; const int N = 505; int mp[N][N], lx[N], ly[N], visx[N], visy[N], match[N]; int n, m, minz, k; bool dfs(int x, int K) { visx[x] = K; for (int y = 1; y <= n; ++y) { if (visy[y] != K && mp[x][y] != INF) { int t = lx[x] + ly[y] - mp[x][y]; if (!t) { visy[y] = K; if (!match[y] || dfs(match[y], K)) { match[y] = x; return true; } } else minz = min(minz, t); } } return false; } void KM() { for (int i = 1; i <= n; ++i) { while (1) { minz = INF; if (dfs(i, ++k)) break; for (int j = 1; j <= n; ++j) { if (visx[j] == k) lx[j] -= minz; if (visy[j] == k) ly[j] += minz; } } } } int main() { scanf("%d%d", &n, &m); memset(mp, INF, sizeof mp); for (int i = 1; i <= n; ++i) lx[i] = -INF; for (int i = 0; i < m; ++i) { int u, v, w; scanf("%d%d%d", &u, &v, &w); mp[u][v] = w; lx[u] = max(lx[u], w); } KM(); ll ans = 0; for (int i = 1; i <= n; ++i) ans += mp[match[i]][i]; printf("%lld\n", ans); for (int i = 1; i <= n; ++i) printf("%d ", match[i]); putchar('\n'); return 0; }

改进版

#include <bits/stdc++.h> using namespace std; typedef long long ll; const int INF = 0x3f3f3f3f; const int N = 505; int mp[N][N], lx[N], ly[N], visy[N], matchy[N], pr[N], slack[N]; int n, m, minz, k; void bfs(int x, int K) { int y = 0, yy = 0; memset(pr, 0, sizeof pr); memset(slack, INF, sizeof slack); matchy[y] = x; while (true) { x = matchy[y]; visy[y] = K; for (int i = 1; i <= n; ++i) { if (visy[i] == K || mp[x][i] == -INF) continue; int t = lx[x] + ly[i] - mp[x][i]; if (slack[i] > t) { slack[i] = t; pr[i] = y; } } minz = INF; for (int i = 1; i <= n; ++i) { if (visy[i] != K && slack[i] < minz) { minz = slack[i]; yy = i; if (!minz) break; } } if (minz) { for(int i = 0; i <= n; ++i) { if (visy[i] == K) lx[matchy[i]] -= minz, ly[i] += minz; else slack[i] -= minz; } } y = yy; if (!matchy[y]) break; } while (y) { matchy[y] = matchy[pr[y]]; y = pr[y]; } } void KM() { for (int i = 1; i <= n; ++i) { bfs(i, ++k); } } int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= n; ++i) { for (int j = 1; j <= n; ++j) { mp[i][j] = -INF; } } for (int i = 1; i <= n; ++i) lx[i] = -INF; for (int i = 0; i < m; ++i) { int u, v, w; scanf("%d%d%d", &u, &v, &w); mp[u][v] = w; lx[u] = max(lx[u], w); } KM(); ll ans = 0; for (int i = 1; i <= n; ++i) ans += mp[matchy[i]][i]; printf("%lld\n", ans); for (int i = 1; i <= n; ++i) printf("%d ", matchy[i]); putchar('\n'); return 0; }

感谢支持!


__EOF__

本文作者炯炯目光
本文链接https://www.cnblogs.com/jjmg/p/13869302.html
关于博主:KTH 信息与网络工程硕士在读
版权声明:欢迎分享或转载
声援博主:To be or not to be, is a question.
posted @   Chiron-zy  阅读(266)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示