博客推荐:KM算法详解        我对KM算法的理解         二分图的最佳完美匹配--KM算法

1、KM算法是用于寻找带权二分图最佳匹配的算法。 对KM算法的描述,基本上可以概括成以下几个步骤: 
(1) 初始化可行标杆 
(2) 用匈牙利算法寻找完备匹配 
(3) 若未找到完备匹配则修改可行标杆 
(4) 重复(2)(3)直到找到相等子图的完备匹配

2、二分图详解:

(1)二分图定义:

所有顶点可以分成两个集:X和Y,其中X和Y中的任意两个在同一个集中的点都不相连,而来自X集的顶点与来自Y集的顶点有连线。当这些连线被赋于一定的权重时,这样的二分图便是带权二分图。

(2)二分图判定:

无向图G为二分图的充分必要条件是,G至少有两个顶点,且其所有回路的长度均为偶数。判断无向连通图是不是二分图,可以使用深度优先遍历算法(又名交叉染色法)。下面着重介绍下交叉染色法的定义与原理

首先任意取出一个顶点进行染色,和该节点相邻的点有三种情况:

a.如果节点没有染过色,就染上与它相反的颜色,推入队列,

b.如果节点染过色且相反,忽视掉,

c.如果节点染过色且与父节点相同,证明不是二分图,return

(3)二分图匹配:

二分图匹配是指求出一组边,其中的顶点分别在两个集合中,且任意两条边都没有相同的顶点,这组边叫做二分图的匹配,而所能得到的最大的边的个数,叫做二分图的最大匹配。 我们也可以换个角度看二分图的最大匹配,即二分图的每条边的默认权重为1,我们求到的二分图的最大匹配的权重最大。对于带权二分图,其边有大于0的权重,找到一组匹配,使其权重最大,即为带权二分图的最佳匹配。

3、增广路径:

(1)增广路径定义比较晦涩难懂,直接看它的特性更清晰一些,增广路径有如下特性:

  • a. 有奇数条边
  • b. 起点在二分图的X边,终点在二分图的Y边
  • c. 路径上的点一定是一个在X边,一个在Y边,交错出现。
  • d. 整条路径上没有重复的点
  • e. 起点和终点都是目前还没有配对的点,其他的点都已经出现在匹配子图中
  • f. 路径上的所有第奇数条边都是目前还没有进入目前的匹配子图的边,而所有第偶数条边都已经进入目前的匹配子图。奇数边比偶数边多一条边
  • g. 于是当我们把所有第奇数条边都加到匹配子图并把条偶数条边都删除,匹配数增加了1.

(2)找增广路径的过程:目前图中只有(X0, Y0)匹配成功,也就是说蓝色这条线是已匹配子图(目前只有一条边),现在X1想要配对,只能和Y0进行配对,但发现Y0已经被X0匹配了,于是就深入到X0,去为X0找新的匹配节点,发现X0可以和Y2匹配,而且Y2处于未匹配状态,因此通过X1找到了增广路径:X1Y0->Y0X0->X0Y2

其中第奇数第边x1y0和x0y2不在当前的匹配子图中,而第偶数条边x0y0在匹配子图中,通过添加x1y0和x0y2到匹配子图并删除x0y0,使得匹配数由1增加到了2。每找到一条增广路径,通过添加删除边,我们总是能使匹配数加1。

增广路径有两种寻径方法,一个是深搜,一个是宽搜。

例如从x2出发寻找增广路径

  • 如果是深搜,x2找到y0匹配,但发现y0已经被x1匹配了,于是就深入到x1,去为x1找新的匹配节点,结果发现x1没有其他的匹配节点,于是匹配失败,x2接着找y1,发现y1可以匹配,于是就找到了新的增广路径。
  • 如果是宽搜,x1找到y0节点的时候,由于不能马上得到一个合法的匹配,于是将它做为候选项放入队列中,并接着找y1,由于y1已经匹配,于是匹配成功返回了。

4、匈牙利算法:

匈牙利算法一般用于寻找二分图的最大匹配。算法根据一定的规则选择二分图的边加入匹配子图中,其基本模式为: 
        初始化匹配子图为空 
        While 找得到增广路径 
        Do 把增广路径添加到匹配子图中

匈牙利算法就是在不断寻找增广路,如果找不到增广路,就说明达到了最大匹配。

匈牙利算法代码:

//---------------------DFS---------------------------------
#include<iostream>
#include<memory.h>
using namespace std;  #define MAXN 10
int graph[MAXN][MAXN];
int match[MAXN];
int visitX[MAXN], visitY[MAXN];
int nx, ny;  bool findPath( int u )
{  visitX[u] = 1;  for( int v=0; v<ny; v++ )  {  if( !visitY[v] && graph[u][v] )  {  visitY[v] = 1;  if( match[v] == -1 || findPath(match[v]) )  {  match[v] = u;  return true;  }  }  }  return false;
}  int dfsHungarian()
{  int res = 0;  memset( match, -1, sizeof(match) );  for( int i=0; i<nx; i++ )  {  memset( visitX, 0, sizeof(visitX) );  memset( visitY, 0, sizeof(visitY) );  if( findPath(i) )  res++;  }  return res;
}  //-----------------------------BFS-------------------------------
#include<iostream>
#include<memory.h>
using namespace std;  #define MAXN 10
int graph[MAXN][MAXN];
//在bfs中,增广路径的搜索是一层一层展开的,所以必须通过prevX来记录上一层的顶点
//chkY用于标记某个Y顶点是否被目前的X顶点访问尝试过。
int matchX[MAXN], matchY[MAXN], prevX[MAXN], chkY[MAXN];
int queue[MAXN];
int nx, ny;  int bfsHungarian()
{  int res = 0;  int qs, qe;  memset( matchX, -1, sizeof(matchX) );  memset( matchY, -1, sizeof(matchY) );  memset( chkY, -1, sizeof(chkY) );  for( int i=0; i<nx; i++ )  {  if( matchX[i] == -1 )   //如果该X顶点未找到匹配点,将其放入队列。  {  qs = qe = 0;  queue[qe++] = i;  prevX[i] = -1;  //并且标记,它是路径的起点  bool flag = 0;  while( qs<qe && !flag )  {  int u = queue[qs];  for( int v=0; v<ny&&!flag; v++ )  {  if( graph[u][v] && chkY[v]!=i ) //如果该节点与u有边且未被访问过  {  chkY[v] = i;    //标记且将它的前一个顶点放入队列中,也就是下次可能尝试这个顶点看能否为它找到新的节点  queue[qe++] = matchY[v];  if( matchY[v] >= 0 )  prevX[matchY[v]] = u;  else    //到达了增广路径的最后一站  {  flag = 1;  int d=u, e=v;  while( d!=-1 )  //一路通过prevX找到路径的起点  {  int t = matchX[d];  matchX[d] = e;  matchY[e] = d;  d = prevX[d];  e = t;  }  }  }  }  qs++;  }  if( matchX[i] != -1 )  res++;  }  }  return res;
}  

5、KM算法:

(1)KM算法,用于求二分图匹配的最佳匹配。何为最佳匹配?就是带权二分图的权值最大的完备匹配称为最佳匹配。 那么何为完备匹配?X部中的每一个顶点都与Y部中的一个顶点匹配,或者Y部中的每一个顶点也与X部中的一个顶点匹配,则该匹配为完备匹配。

KM算法的最大特点在于利用标杆和权重来生成一个二分子图,在该二分子图上面找最大匹配,而且,当些仅当找到完备匹配,才能得到最佳匹配。标杆和权重的作用在于限制新边的加入,使得加入的新边总是能为子图添加匹配数,同时又令权重和得到最大的提高。

(2)KM算法步骤:

  • a.用邻接矩阵(或其他方法也行啦)来储存图,注意:如果只是想求最大权值匹配而不要求是完全匹配的话,请把各个不相连的边的权值设置为0。
  • b.运用贪心算法初始化标杆。
  • c.运用匈牙利算法找到完备匹配。
  • d.如果找不到,则通过修改标杆,增加一些边。
  • e.重复c,d的步骤,直到完全匹配时可结束。

(3)KM算法顶标

二分图最佳匹配还是二分图匹配,所以跟和匈牙利算法思路差不多。二分图是特殊的网络流,最佳匹配相当于求最大(小)费用最大流,所以FF算法(全名Ford-Fulkerson算法)也能实现。

  • 所以我们可以把这匈牙利算法和FF算法结合起来。这就是KM算法的思路了:尽量找最大的边进行连边,如果不能则换一条较大的。
  • FF算法里面,我们每次是找最长(短)路进行通流,所以二分图匹配里面我们也按照FF算法找最大边进行连边!

但是遇到某个点被匹配了两次怎么办?那就用匈牙利算法进行更改匹配!所以,根据KM算法的思路,我们一开始要对边权值最大的进行连线。那问题就来了,我们如何让计算机知道该点对应的权值最大的边是哪一条?或许我们可以通过某种方式记录边的另一端点,但是呢,后面还要涉及改边,又要记录边权值总和,而这个记录端点方法似乎有点麻烦。于是KM采用了一种十分巧妙的办法(也是KM算法思想的精髓):添加标杆(顶标)

添加标杆(顶标)流程:

  • 我们对左边每个点Xi和右边每个点Yi添加标杆Cx和Cy。其中我们要满足Cx+Cy>=w[x][y](w[x][y]即为点Xi、Yi之间的边权值)
  • 对于一开始的初始化,我们对于每个点分别进行如下操作:Cx=max(w[x][y]);  Cy=0;

添加顶标之前的二分图:

添加顶标之后的二分图:

(4)KM算法原理、流程、伪代码

KM算法--学习笔记相关推荐

  1. 大顶堆删除最大值_算法学习笔记(47): 二叉堆

    堆(Heap)是一类数据结构,它们拥有树状结构,且能够保证父节点比子节点大(或小).当根节点保存堆中最大值时,称为大根堆:反之,则称为小根堆. 二叉堆(Binary Heap)是最简单.常用的堆,是一 ...

  2. Manacher算法学习笔记 | LeetCode#5

    Manacher算法学习笔记 DECLARATION 引用来源:https://www.cnblogs.com/grandyang/p/4475985.html CONTENT 用途:寻找一个字符串的 ...

  3. 数据结构与算法学习笔记之 从0编号的数组

    数据结构与算法学习笔记之 从0编号的数组 前言 数组看似简单,但掌握精髓的却没有多少:他既是编程语言中的数据类型,又是最基础的数据结构: 一个小问题: 为什么数据要从0开始编号,而不是 从1开始呢? ...

  4. 输出dag的所有拓扑排序序列_算法学习笔记(53): 拓扑排序

    拓扑排序是对DAG(有向无环图)上的节点进行排序,使得对于每一条有向边 , 都在 之前出现.简单地说,是在不破坏节点 先后顺序的前提下,把DAG拉成一条链.如果以游戏中的科技树(虽然名字带树,其实常常 ...

  5. 算法学习笔记:对指定金额计算最少钞票数

    算法学习笔记:对指定金额计算最少钞票数 一.引出问题 财务人员给员工发工资时经常遇到这样一个问题,即根据每个人的工资额(以元作为单位)计算出各种面值的钞票的张数,且要求总张数最少.例如,某职工工资为3 ...

  6. matlab中x从0到5不含0,关于MATLAB的数学建模算法学习笔记

    关于MATLAB的数学建模算法学习笔记 目录 线性规划中应用: (3) 非线性规划: (3) 指派问题;投资问题:(0-1问题) (3) 1)应用fmincon命令语句 (3) 2)应用指令函数:bi ...

  7. 机器学习篇01:在线学习的支持向量机算法学习笔记

    在线学习的支持向量机算法学习笔记 oisvm算法实现说明 oisvm算法实现说明 % 本程序是用于实现基于在线学习的调制信号识别的程序 % % % 第一步:调制信号的生成 % 首先是7个信号:2ASK ...

  8. 数据结构与算法学习笔记之 提高读取性能的链表(上)

    数据结构与算法学习笔记之 提高读取性能的链表(上) 前言 链表(Linked list)比数组稍微复杂一点,在我们生活中用到最常见的应该是缓存,它是一种提高数据读取性能的技术,常见的如cpu缓存,浏览 ...

  9. l2-004 这是二叉搜索树吗?_算法学习笔记(45): 二叉搜索树

    二叉搜索树(Binary Search Tree, BST)是一种常用的数据结构,在理想情况下,它可以以 的复杂度完成一系列修改和查询,包括: 插入一个数 删除一个数 查询某数的排名(排名定义为比该数 ...

  10. 两个字符串的最长公共子序列长度_算法学习笔记(58): 最长公共子序列

    (为什么都更了这么多篇笔记了,这时候才讲这么基础的内容呢?因为我本来以为LCS这种简单的DP不用讲的,结果CF不久前考了LCS的变式,然后我发现由于自己对LCS一点都不熟,居然写不出来 ,于是决定还是 ...

最新文章

  1. 2021年大数据ELK(三):Lucene全文检索库介绍
  2. c++文件读取空格_程序员术与道:术—C语言对文件进行处理,文件处理的基本操作...
  3. mongodb创建用户名和密码_Python中使用MongoDB详解
  4. node-red教程2 第一条数据流
  5. TCP/IP 5.3.5 认证
  6. html页面 wordpress,WordPress纯代码实现前端页面HTML完美压缩
  7. 95-36-100-ChannelHandler-ChannelOutboundHandler
  8. 【转】常见面试之机器学习算法思想简单梳理
  9. 最短寻道时间优先算法c语言程序,操作系统先来先服务、最短寻道时间优先(SSTf)、扫描算法(SCAN)、循环扫描算法(CSCAN)的c++实现.doc...
  10. 产品经理该如何做竞品分析
  11. java图片转ASCII码_将图片转化成对应的Ascii字符图片
  12. java ajax动态加载数据_java实现的highcharts与ajax结合动态实时获取数据更新图表
  13. Atitit. 解释器模式框架选型 and应用场景attilax总结 oao
  14. Dorado7 首页菜单CSS调整
  15. 2019 FeatherNets: Convolutional Neural Networks as Light as Feather for Face Anti-spoofing
  16. TypeError: object() takes no parameters
  17. 超详细的wireshark笔记(6)-UDP协议
  18. CentOS 单机安装Zookeeper-3.4.13
  19. java-Map接口
  20. 用Java语言开发物联网设备应用(5)

热门文章

  1. Filezilla Server使用教程
  2. Day 1:矩阵归零消除序列和
  3. 基于matlab的信号频谱分析 开题报告,基于MATLAB的数字信号处理开题报告
  4. java监控屏幕_Java实现简单屏幕监控
  5. python做excel数据分析统计服_怎样用 Excel 做数据分析?
  6. excel使用教程_正版办公软件教程书Word Excel PPT办公应用从入门到精通教学加视频!...
  7. 网络语言维c是什么意思,我不要你觉得,我要我觉得!19年网络流行词是这些!...
  8. java hsqldb_Java HsqlDB的初步使用和技巧总结
  9. 析论易语言软件加密技术(创世纪篇)
  10. 推荐几个适合 新手学习 软件逆向 脱壳破解 的网站