KM算法的前提是图存在一个完备匹配,因此用于二分图的最佳匹配问题。如果是最大权匹配问题,可以通过加权值为0的边来可以将图的最佳匹配与最大全匹配统一起来;如果是最小权匹配问题,可以通过加权值为-INF的边来可以将图的最佳匹配与最大全匹配统一起来(HDU 1853)。见:完美匹配,完备匹配,最佳匹配,最大权匹配
如果我们要求边权值最小的匹配可以把边权值取负值,得出结果后再取相反数即为答案。
KM算法求得的最大权匹配是边权值和最大,如果想要边权之积最大,每条边权取自然对数,然后求最大和权匹配,求得的结果a再算出e^a就是最大积匹配。
题集:
poj 3565
hdu 2255
hdu 1533
hdu 3488 hdu1853 hdu 3435
hdu 2426
hdu 2853
hdu 3718
hdu 3722
hdu 3395
hdu 2282
hdu 2813
hdu 2448
hdu 2236
hdu 3315
hdu 3523
KM算法各版本模板:
dfs版KM算法模板O(n4)复杂度
图解
图解+模板
对访问过且匹配过的男生,期望值要加上女生减去的期望值;代码中的未访问过的男生实际上包含两种,一种是完全没被访问过的(slack[ j ] = INF),一种是访问过,且更新过slack值但没有参与匹配的男生,由于这种男生可匹配的部分女生期望值降低,所以该男生的slack值减少。
HDU 2255 模板

#include <iostream>
#include <cstring>
#include <cstdio>using namespace std;
const int MAXN = 305;
const int INF = 0x3f3f3f3f;
// 女生视为左图点,男生视为右图点
int love[MAXN][MAXN];   // 记录每个妹子和每个男生的好感度
int ex_girl[MAXN];      // 每个妹子的期望值
int ex_boy[MAXN];       // 每个男生的期望值
bool vis_girl[MAXN];    // 记录每一轮匹配匹配过的女生
bool vis_boy[MAXN];     // 记录每一轮匹配匹配过的男生
int match[MAXN];        // 记录每个男生匹配到的妹子 如果没有则为-1
int slack[MAXN];        // 记录每个汉子如果能被妹子倾心最少还需要多少期望值int N;bool dfs(int girl)
{vis_girl[girl] = true;for (int boy = 0; boy < N; ++boy) {if (vis_boy[boy]) continue; // 每一轮匹配 每个男生只尝试一次int gap = ex_girl[girl] + ex_boy[boy] - love[girl][boy];if (gap == 0) {  // 如果符合要求vis_boy[boy] = true;if (match[boy] == -1 || dfs( match[boy] )) {    // 找到一个没有匹配的男生 或者该男生的妹子可以找到其他人match[boy] = girl;return true;}} else {slack[boy] = min(slack[boy], gap);  // slack 可以理解为该男生要得到女生的倾心 还需多少期望值 取最小值 备胎的样子【捂脸}}return false;
}int KM()
{memset(match, -1, sizeof match);    // 初始每个男生都没有匹配的女生memset(ex_boy, 0, sizeof ex_boy);   // 初始每个男生的期望值为0// 每个女生的初始期望值是与她相连的男生最大的好感度for (int i = 0; i < N; ++i) {ex_girl[i] = love[i][0];for (int j = 1; j < N; ++j) {ex_girl[i] = max(ex_girl[i], love[i][j]);}}// 尝试为每一个女生解决归宿问题for (int i = 0; i < N; ++i) {fill(slack, slack + N, INF);    // 因为要取最小值 初始化为无穷大while (1) {// 为每个女生解决归宿问题的方法是 :如果找不到就降低期望值,直到找到为止// 记录每轮匹配中男生女生是否被尝试匹配过memset(vis_girl, false, sizeof vis_girl);memset(vis_boy, false, sizeof vis_boy);if (dfs(i)) break;  // 找到归宿 退出// 如果不能找到 就降低期望值// 最小可降低的期望值int d = INF;for (int j = 0; j < N; ++j)if (!vis_boy[j]) d = min(d, slack[j]);for (int j = 0; j < N; ++j) {// 所有访问过的女生降低期望值if (vis_girl[j]) ex_girl[j] -= d;// 所有访问过的男生增加期望值if (vis_boy[j]) ex_boy[j] += d;// 没有访问过的boy 因为girl们的期望值降低,距离得到女生倾心又进了一步!else slack[j] -= d;}}}// 匹配完成 求出所有配对的好感度的和int res = 0;for (int i = 0; i < N; ++i)res += love[ match[i] ][i];return res;
}int main()
{while (~scanf("%d", &N)) {for (int i = 0; i < N; ++i)for (int j = 0; j < N; ++j)scanf("%d", &love[i][j]);printf("%d\n", KM());}return 0;
}

优化dfs版KM算法O(n3)
参考博客
HDU 2255模板

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define IO ios::sync_with_stdio(false)typedef long long ll;using namespace std;
const int INF = 0x3f3f3f3f;
const int mn=305;
const ll mod=1000000007;
int match[mn],lx[mn],ly[mn],slack[mn],fa[mn*3];
int G[mn][mn];
bool visx[mn],visy[mn];
int n,nx,ny;int findpath(int x)
{int tempDelta;visx[x]=true;for(int y=1;y<=ny;y++){if(visy[y])continue;tempDelta =lx[x]+ly[y]-G[x][y];if(tempDelta ==  0){visy[y] = true;fa[y+nx]=x;if(match[y] == -1){return y+nx;}fa[match[y]]=y+nx;//记录交替树的父亲信息(为了区别X,Y集合,Y的点都映射成n+y)int res=findpath(match[y]);if(res>0)return res;//返回增广路的末端叶子节点}else if(slack[x] > tempDelta)//统计以x为准的slack值。slack[x] = tempDelta;}return -1;
}
void KM()
{for(int x = 1 ; x <= nx ; ++x){for(int i = 1 ; i <= nx ; ++i) slack[i] =INF;for(int i=1;i<=nx+ny;i++)fa[i]=-1;memset(visx,false,sizeof(visx));memset(visy,false,sizeof(visy));//换到外面,可以保留原树int fir=1;int leaf=-1;while(true){if(fir==1){leaf=findpath(x);fir=0;}else{for(int i=1;i<=nx;i++){if(slack[i]==0){//只接着搜有新边加入的X点slack[i]=INF;//slack要重新清空,方以后接着用leaf=findpath(i);if(leaf>0)break;}}}if(leaf>0){int p=leaf;while(p>0){match[p-nx]=fa[p];p=fa[fa[p]];//顺着记录一路找找上去}break;}else{int delta =INF;for(int i = 1 ; i <= nx ; ++i)if(visx[i] && delta > slack[i])delta = slack[i];for(int i = 1 ; i <= nx ; ++i)if(visx[i]) {lx[i] -= delta;slack[i]-=delta;}//X点的slack要响应改变,slack变0说明有新边加入for(int j = 1 ; j <= ny ; ++j){if(visy[j])ly[j] += delta;}}}}
}int solve()
{   //初始化: memset(match,-1,sizeof(match));memset(ly,0,sizeof(ly));memset(fa,0,sizeof(fa));for(int i = 1 ; i <= nx ; ++i){lx[i] = -1;for(int j = 1 ; j <= ny ; ++j)if(lx[i] < G[i][j])lx[i] = G[i][j];}KM();int ans=0;for(int i=1;i<=ny;++i){if(match[i]!=-1) ans+=G[match[i]][i];} return ans;
}
int main()
{int n;while(~scanf("%d",&n)){nx=n;ny=n;//nx左部图点数,ny右部图点数for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){scanf("%d",&G[i][j]);}}int ans=solve();printf("%d\n",ans);}return 0;
}

bfs版KM算法模板O(n3)复杂度
HDU 2255模板

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define IO ios::sync_with_stdio(false)
typedef unsigned long long ull;//卡精度
const int N = 607;//最多图的点数
const int mod = 1e9+7;
const int INF = 1e9+7;//若求最小权值匹配,应该为-1e9+7;
int w[N][N];//边权
int la[N], lb[N];//左、右部点的顶标
bool va[N], vb[N];//访问标记,是否在交错树中
int match[N];//右部点匹配的左部点(一个只能匹配一个嘛)
int n;
int delta, upd[N];
int p[N];
int c[N];void bfs(int x)
{int a, y = 0, y1 = 0;for(int i = 1; i <= n; ++ i)p[i] = 0, c[i] = INF;match[y] = x;do{a = match[y], delta = INF, vb[y] = true;for(int b = 1; b <= n; ++ b){if(!vb[b]){if(c[b] > la[a] + lb[b] - w[a][b])c[b] = la[a] + lb[b] - w[a][b], p[b] = y;if(c[b] < delta)//Δ还是取最小的delta = c[b], y1 = b;}}for(int b = 0; b <= n; ++ b)if(vb[b])la[match[b]] -= delta, lb[b] += delta;else c[b] -= delta;y = y1;}while(match[y]);while(y)match[y] = match[p[y]], y = p[y];
}int KM()
{for(int i = 1; i <= n; ++ i)match[i] = la[i] = lb[i] = 0;for(int i = 1; i <= n; ++ i){for(int j = 1; j <= n; ++ j)vb[j] = false;bfs(i);}int res = 0;for(int y = 1; y <= n; ++ y)//若匹配失败w[match[y]][y]=INF;res += w[match[y]][y];return res;
}int main()
{while(~scanf("%d", &n)){for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){scanf("%d",&w[i][j]);}} printf("%d\n", KM());}return 0;
}

各版本比较
首先看三个版本在HDU2255的表现:
dfs版:

优化dfs版:

bfs版:

在2019ICPC南京区域赛J题的表现:
dfs版:

优化dfs版:

bfs版:

KM算法(DFS版,优化DFS版,BFS版)相关推荐

  1. NOI数据结构:KM算法

    NOI数据结构:二分图的最佳匹配KM算法 二分图匹配之最佳匹配--KM算法 - ~Lanly~ - 博客园 二分图最佳完美匹配-KM算法_ZigZagK的博客-CSDN博客_完美匹配2 二分图的最佳完 ...

  2. 【算法笔记】二分图最大权匹配 - KM算法(dfs版O(n4) + bfs版O(n3))

    整理的算法模板合集: ACM模板 匈牙利算法又称为 KM 算法,可以在 O(n3)O(n^3)O(n3) 时间内求出二分图的 最大权完美匹配 . 考虑到二分图中两个集合中的点并不总是相同,为了能应用 ...

  3. LeetCode算法题13:DFS/BFS - 单词搜索

    文章目录 单词搜索 DFS : 小小的优化 总结 单词搜索 题目链接:https://leetcode-cn.com/problems/word-search/ 题目描述: 给定一个 m x n 二维 ...

  4. matlab中fic算法,粒子群算法在函数优化问题中的应用最终版(全文完整版)

    <粒子群算法在函数优化问题中的应用.doc>由会员分享,可免费在线阅读全文,更多与<粒子群算法在函数优化问题中的应用(最终版)>相关文档资源请在帮帮文库(www.woc88.c ...

  5. 算法第十期——DFS(深度优先搜索)的剪枝优化

    目录 DFS:剪枝 DFS:有哪些剪枝方法 DFS例题一:剪格子 [思路] DFS例题二:路径之谜 [样例分析] DFS例题三:四阶幻方 [思路] [做法一] [做法二] DFS例题三:分考场 [样例 ...

  6. c语言中dfs算法不定起点问题,dfs算法(dfs算法例子)

    有哪位大侠知道请告知 首先选定图的类别(有向图.无向图),再选定图的存储结构,根据输入的顶点或者. 根据已有的邻接矩阵或邻接表用递归方法编写深度优先搜索遍历算法,并输出遍历结. 要求思路!谢谢! 深度 ...

  7. c语言中dfs用pos做参数,LeetCode算法练习——深度优先搜索 DFS(2)

    更多干货就在我的个人博客 BlackBlog.tech 欢迎关注! 也可以关注我的csdn博客:黑哥的博客 谢谢大家! 我们继续LeetCode之旅. 做了一段时间的LeetCode,感觉还是不错的. ...

  8. 【算法】蓝桥杯dfs深度优先搜索之排列组合总结

    [导航] 上一篇文章 → <[算法]蓝桥杯dfs深度优先搜索之凑算式总结>   为了重申感谢之意,再次声明下文的大部分灵感均来自于[CSDN]梅森上校<JAVA版本:DFS算法题解两 ...

  9. 新书推荐 |《机器学习:算法视角(原书第2版)》

    新书推荐 <机器学习:算法视角(原书第2版)> 长按二维码 了解及购买 CRC Press机器学习领域畅销教材: 知名媒体推荐的十大机器学习入门教材之一: 新西兰惠灵顿维多利亚大学数学与统 ...

最新文章

  1. Ubuntu(Linux) 下 unzip 命令使用详解
  2. Data Lake Analytics: 读/写PolarDB的数据
  3. 使用参数化SQL语句进行模糊查找
  4. js前面代码出错继续运行_Node.JS实战41:让命令行变的五颜六色
  5. hibernate3.4+struts1.3分页封装,有兴趣者可以看一下
  6. 命令行以及git基础使用
  7. 9.1 ps:查看进程
  8. keepalived配置文件详解   ​
  9. vue项目中z-index不起作用(将vue实例挂在到window上面)
  10. 前端学习(2730):重读vue电商网站40之使用vue-table-with-tree-grid
  11. 渗透测试入门14之渗透测试工具1
  12. Facebook 发布全新 JavaScript 引擎:Hermes! | CSDN博文精选
  13. 好文要顶之 --- 简单粗暴地理解 JavaScript 原型链
  14. struts入门实例
  15. 嵌入式Linux开发笔记(韦东山2)
  16. python 课程设计扫雷报告_扫雷游戏课程设计报告
  17. UML-包图中包与包之间的关系
  18. Xcode7-“App Transport Security has blocked a cleartext HTTP (http://) resource load since it is inse
  19. 新浪微博技术架构分析-转载
  20. windows系统 对应GVLK码自查

热门文章

  1. 怎么把计算机桌面的文件固定到任务栏,win10如何将桌面我的电脑固定到任务栏 - 卡饭网...
  2. 浏览器的Response Header
  3. App隐私政策网址(URL)
  4. Elasticsearch分页解决方案研究
  5. 推荐几个比较有意思的js效果插件
  6. 桌面开始和快捷方式消失了
  7. [ARC089E] GraphXY
  8. RGD-全氟化碳纳米乳MRI显影剂/标记乳糖基白蛋白的超顺磁性氧化铁粒子(LAC-HSA-SPIO)
  9. Material studio切胞操作
  10. 阿里巴巴的邮箱的服务器是什么意思啊,客户端设置中IMAP和POP3有什么不同呢?...