KM 最优匹配 讲解

转:

基本原理

  该算法是通过给每个顶点一个标号(叫做顶标)来把求最大权匹配的问题转化为求完备匹配的问题的。设顶点Xi的顶标为A[ i ],顶点Yj的顶标为B[ j ],顶点Xi与Yj之间的边权为w[i,j]。在算法执行过程中的任一时刻,对于任一条边(i,j),A[ i ]+B[j]>=w[i,j]始终成立。

 

  KM算法的正确性基于以下定理:

 

  若由二分图中所有满足A[ i ]+B[j]=w[i,j]的边(i,j)构成的子图(称做相等子图)有完备匹配,那么这个完备匹配就是二分图的最大权匹配。

 

  这个定理是显然的。因为对于二分图的任意一个匹配,如果它包含于相等子图,那么它的边权和等于所有顶点的顶标和;如果它有的边不包含于相等子图,那么它的边权和小于所有顶点的顶标和。所以相等子图的完备匹配一定是二分图的最大权匹配。

 

  初始时为了使A[ i ]+B[j]>=w[i,j]恒成立,令A[ i ]为所有与顶点Xi关联的边的最大权,B[j]=0。如果当前的相等子图没有完备匹配,就按下面的方法修改顶标以使扩大相等子图,直到相等子图具有完备匹配为止。

 

  我们求当前相等子图的完备匹配失败了,是因为对于某个X顶点,我们找不到一条从它出发的交错路。这时我们获得了一棵交错树,它的叶子结点全部是X顶点。现在我们把交错树中X顶点的顶标全都减小某个值d,Y顶点的顶标全都增加同一个值d,那么我们会发现:

 

  1)两端都在交错树中的边(i,j),A[ i ]+B[j]的值没有变化。也就是说,它原来属于相等子图,现在仍属于相等子图。

 

  2)两端都不在交错树中的边(i,j),A[ i ]和B[j]都没有变化。也就是说,它原来属于(或不属于)相等子图,现在仍属于(或不属于)相等子图。

 

  3)X端不在交错树中,Y端在交错树中的边(i,j),它的A[ i ]+B[j]的值有所增大。它原来不属于相等子图,现在仍不属于相等子图。

 

  4)X端在交错树中,Y端不在交错树中的边(i,j),它的A[ i ]+B[j]的值有所减小。也就说,它原来不属于相等子图,现在可能进入了相等子图,因而使相等子图得到了扩大。

 

  现在的问题就是求d值了。为了使A[ i ]+B[j]>=w[i,j]始终成立,且至少有一条边进入相等子图,d应该等于:

 

  Min{A[ i ]+B[j]-w[i,j] | Xi在交错树中,Yi不在交错树中}。

 

改进

  以上就是KM算法的基本思路。但是朴素的实现方法,时间复杂度为O(n4)——需要找O(n)次增广路,每次增广最多需要修改O(n)次顶标,每次修改顶标时由于要枚举边来求d值,复杂度为O(n2)。实际上KM算法的复杂度是可以做到O(n3)的。我们给每个Y顶点一个“松弛量”函数slack,每次开始找增广路时初始化为无穷大。在寻找增广路的过程中,检查边(i,j)时,如果它不在相等子图中,则让slack[j]变成原值与A[ i ]+B[j]-w[i,j]的较小值。这样,在修改顶标时,取所有不在交错树中的Y顶点的slack值中的最小值作为d值即可。但还要注意一点:修改顶标后,要把所有的不在交错树中的Y顶点的slack值都减去d。

 

 

Kuhn-Munkras算法流程:

 

  (1)初始化可行顶标的值

 

  (2)用匈牙利算法寻找完备匹配

 

  (3)若未找到完备匹配则修改可行顶标的值

 

  (4)重复(2)(3)直到找到相等子图的完备匹配为止

 1 bool find(int x)
 2 {//匈牙利算法寻找x的增广路径 
 3 //以x为根的M的交错树 
 4 //看来本算法需要二部图两部分的顶点个数都相等吧 
 5     int y, t;
 6     visitx[x] = true;
 7     for( y = 0; y < N; y++ )
 8     {
 9        if( visity[y] ) continue;//找增广路径的过程中不妨问已经访问过的顶点 
10        t = lx[x] + ly[y] - w[x][y];//是在等子图中寻找匹配的增广路径 
11        if( t == 0 )
12        {
13           visity[y] = true;
14           if( linky[y] == -1 || find(linky[y]) )
15           {
16              linky[y] = x;
17              return true;
18           }
19        }
20         else
21         {//因为本来就需要将一条x顶点在交错树中,y顶点不在交错树中的边扩展进交错树来
22         //所以只改变这些不在等子图中的边的y顶点的松弛量 
23            if( slack[y] > t )
24            slack[y]=t;
25         }
26      }
27      return false;
28 }
29 //外层的匈牙利算法需要O(2)的时间,而修改顶标时由于要枚举所有的边所以也需要O(2)的时间
30 //所以总时间是O(4) 
31 //引入松弛量以后改变顶标就不需要枚举每一条边,只需要枚举不在交错树中的y的松弛量,所以
32 //时间复杂度降为O(3) 
33 void KM()
34 {//KM算法寻找图的最大权匹配 
35      int i, j, x, d;
36      memset(linky,-1,sizeof(linky));
37      memset(lx,0,sizeof(lx)); //x的顶标 
38      memset(ly,0,sizeof(ly));//y的顶标 
39      for( i = 0; i < N; i++)
40         for( j = 0; j < N; j++ )
41            if( map[i][j] > lx[i] )
42              lx[i] = map[i][j];//一开始x的顶标为所有与x相连的边中权值最大的边的权值,y的顶标为0 
43         for( x = 0; x < N; x++ )
44         {//在匈牙利算法中从每个x出发寻找增广路,如果找到就在匹配值上加1,这是为了寻找最大匹配
45         //而在此处,必须找到完备匹配,所以对于每一个x中的顶点,找到其增广路就跳出,找不到的话
46         //就需要修改顶标值直至找到为止 
47             for( i = 0; i < N; i++ )
48                slack[i] = INF;//松弛变量 
49            while (true)
50            {//无限循环直至找到完备匹配 
51                 memset(visitx, 0, sizeof(visitx));//visx为真表示的是该顶点是匹配中的顶点 
52                 memset(visity, 0, sizeof(visity));//y同理 
53                 if( find(x) ) break;
54                 d = INF;
55                 for( i = 0; i < N; i++) 
56                 {
57                    if ( !visity[i] )//注意是取所有不在交错树中的y顶点的松弛量的最小值作为d的值 
58                      if ( d > slack[i] )
59                         d = slack[i];
60                 }
61                 for( i = 0; i < N; i++ )
62                 {
63                    if( visitx[i] )
64                      lx[i] -= d;
65                 }
66                 for( i = 0; i < N; i++ )
67                 {
68                    if( visity[i] )
69                    ly[i] += d;
70                    else
71                    slack[i] -= d;
72                 }
73             }
74         }
75 }

转载于:https://www.cnblogs.com/shenshuyang/archive/2012/08/07/2626102.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/401379.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端开发问题记录

小程序开发问题记录多行省略&#xff08;小程序&#xff09;image 图片底部留白单元素如何实现&#xff1a;文本、边框渐变&#xff1b;且边框满足移动端细边框效果&#xff08;小程序&#xff09;在util.js中使用getApp()这个函数&#xff0c;打印显示undefined多行省略 &…

长沙理工大学校园网客户端无法卸载解决办法

2019独角兽企业重金招聘Python工程师标准>>> 删除X:\Program Files (x86)\InstallShield Installation Information\{。。。}目录即可。&#xff08;&#xff09; 转载于:https://my.oschina.net/ZaneYoung/blog/330747

Struts2——namespace、action、以及path问题

简单的介绍下Struts2中的几个简单的问题&#xff08;namespace、action、以及path问题&#xff09; namespace&#xff08;命名空间&#xff09; Namespace决定了action的访问路径&#xff0c;默认为“”&#xff0c;意味着可以访问所有目录下的/ass/sss/ss/index;囊括了所有pa…

About IndexDB(转)

IndexedDB是用于客户端的大量的结构化数据存储和使用索引高效率搜索数据的API&#xff0c;它是基于W3C拟定的草案索引数据库的API。相对DOM存储的小存储数据量&#xff0c;IndexedDB具有大容量的数据存储功能&#xff0c;它分别为同步数据和异步数据提供的API&#xff0c;但目前…

文章收藏(一)

用 Yarn 你还能做这 5 件事 [译] 在 JavaScript 领域中有几个包管理器: npm&#xff0c;bower&#xff0c;component&#xff0c;和 volo。到本文为止&#xff0c;最受欢迎的包管理器是 npm。npm 客户端提供了对 npm 注册库中成千上万代码的访问。Facebook 推出了一款名叫 Yarn…

前端 进阶

前端 进阶一、HTMLmetaviewport[题] meta标签&#xff0c;实现页面自动刷新/跳转二、CSSCSS选择器CSS选择器匹配原理CSS优先级 / 权重可继承 / 不可继承属性盒模型offsetWidth、clientWidth、scrollWidth**box-sizing属性BFC块级格式化上下文position定位实现水平居中实现垂直居…

Windows文件被占用解决办法

我们有时会遇到某个文件被占用&#xff0c;无法删除或者修改。很多人此时重启机器来解决&#xff0c;但是因为有的程序已启动就把文件占用了&#xff0c;重启也没用。 其实&#xff0c;我们可以使用perfmon.exe /res 在上面的搜索框里输入被占用的文件名&#xff0c;就可以知道…

配置yum,nc,telnet

一、学习中问题   最近学习在学习Hadoop的一个子项目Zookeeper&#xff0c;在测试其中的“四字命令”---”echo ruok|nc localhost 2181“时发现命令无法被识别&#xff0c;如下图所示&#xff1a; [roothadoop ~]# echo ruok|nc localhost 2181 -bash: nc: command not foun…

调用方法mssql_fetch_row、mssql_fetch_array、mssql_fetch_assoc和mssql_fetch_objcect读取数据的区别...

方法名&#xff1a;mssql_fetch_row() 测试&#xff1a; 返回&#xff1a; Notice: Undefined index: UserId in D:/_PHP_Test/Test2/test_connLocalDB.php on line 32 ::王小一Notice: Undefined index: UserId in D:/_PHP_Test/Test2/test_connLocalDB.php on line 32 ::王小…

20145318赵一《网络对抗》后门原理与实践

20145318赵一《网络对抗》后门原理与实践 知识点 后门 后门程序一般是指那些绕过安全性控制而获取对程序或系统访问权的程序方法。  在软件的开发阶段&#xff0c;程序员常常会在软件内创建后门程序以便可以修改程序设计中的缺陷。但是&#xff0c;如果这些后门被其他人知道&…

Nagios:企业级系统监控方案

在大多数情况下Cacti RRDtool已经实现对系统各种参数的监测。但很多企业可能不满足于仅仅监测系统基本参数的需求&#xff0c;而是需要监测除基本参数之外的各种应用程序的运行状况。很显然在这种情况下对于一些系统或者是自定义的程序Cacti RRDtool的局限性就显示出来了。而…

Universal-Image-Loader解析(二)——DisplayImageOptions的详细配置与简单的图片加载...

在使用这个框架的时候&#xff0c;我们必须要配置一个DisplayImageOptions对象来作为ImageLoader.getInstance().displayImage&#xff08;&#xff09;中的参数&#xff0c;所以很有必要讲解这个对象的配制方法。讲解完了后其实这个框架我们就会了解的比较详尽了。 1.默认的配…

React 进阶

React 进阶一、认识 React1、是什么&#xff1f;2、React 特性3、React 第一个实例&#xff08;HTML 模板&#xff09;4、React 安装二、React 核心1、JSX2、元素渲染3、组件4、Props5、State6、组件生命周期7、事件处理8、条件渲染9、列表 & Key10、表单11、状态提升12、组…

dropMenu----简单的下拉菜单生成器

HTML <div class"input-group"><span class"input-group-addon" style"width: 100px" >职级&#xff1a;</span><input type"text" class"units form-control" id"jobTitle" value"其…

linux之Vim使用

Vim同Emac是Linux世界下最为流行的两个文本编辑工具&#xff0c;集中精力学习一个就好了&#xff0c;暂定以Vim为学习对象。在本文中&#xff0c;一些基本的操作将不再介绍&#xff0c;只会介绍最为常用的命令以及设置&#xff0c;操作系统为Ubuntu 12.04. Vim的默认配置&#…

Holedox Moving

2012-08-11 我的第一个A*算法&#xff1a; 四处看A*算法。。还是有一点没有弄明白就是那个当已经在列表中的时候再次进入的时候怎么去更新。 这道题。。有点难开始的时候不会位压缩&#xff0c;去看了一个别人的代码。所以感谢一下。这位高手。写了一个bfs(),500多ms。 看了A*…

mint mvc文件上传功能——使用篇

为什么80%的码农都做不了架构师&#xff1f;>>> 为了不打击大家的积极性&#xff0c;暂时只着重讲用法&#xff0c;原理方面暂时不讲太多。 配置web.xml 文件上传需要用到servlet3的异步处理功能。需要在web.xml配置文件中加入异步支持声明&#xff08;注释处&am…

React 项目开发问题积累

React 开发问题积累1. 修改antd的组件样式2. antd级联选择框&#xff08;后台数据渲染&#xff09;1. 修改antd的组件样式 问题&#xff1a;直接修改样式好像不起作用&#xff0c;直接在组件上加style行内样式也不生效 方案&#xff1a;用 :global样式穿透 全局样式直接使用 …

关于spring中util:/的配置

解决redis设置缓存时间找到的帖子&#xff0c;我这个初学者需要学习的还是很多的。 原文地址&#xff1a;http://www.doc100.net/bugs/t/216322/index.html 探索<util/>命名空间 事情的发展总是一段曲折前进的过程。当Spring刚出现时&#xff0c;开发者可以使用<…

静态类

静态类必须直接从System.Object派生&#xff0c;从其他任何基类派生没有任何意义。无法创建静态类的实例静态类不能实现任何接口&#xff0c;这是因为只有使用类的一个实例时&#xff0c;才可以调用类的接口方法静态类只能定义静态成员&#xff08;字段、方法、属性和事件&…