自己在写一个小程序时,遇到了一个类似于“完备匹配下的最大权匹配”的优化问题。在网上搜了下相关资料,了解到对应的匈牙利算法与KM算法,而且已经都有大神进行了详细讲解和代码的编写。唯一的不同之处是我参考的文章中KM算法目标是匹配结果最大为目标,而我的程序中是以匹配结果最小为目标。自己把代码改写了下,并封装为类。验证结果表明代码没有问题~

这里将代码放出,以供网友参考。不过具体到算法层,本人理解不深,还是观摩网上算法大神的讲解(见后面参考文献)吧。

1)KM.h

#ifndef _KM_H_
#define _KM_H_#include <vector>class KM
{
public:KM();~KM();void init(int size, std::vector<std::vector<double>> diff);bool dfs(int a);void KM_Calc(); int* getMatch();double getdiffSum();private:std::vector<std::vector<double>> diff;// 差异度矩阵(方阵)double* ex_a;// 每个a类对象的期望double* ex_b;// 每个b类对象的期望bool* vis_a;// 记录每一轮匹配过的a类对象bool* vis_b;// 记录每一轮匹配过的b类对象int* match;// 记录每一个b类对象匹配到的a类对象索引,如果没有匹配到则为-1double* slack;// 记录每个b类对象能被a类对象匹配到所需要减少的最小差异度int size;// 记录差异度矩阵的边长double diffSum;// 记录匹配结果的总体差异度大小
};#endif

2)KM.cpp

#include "KM.h"KM::KM()
{// 参数初始化ex_a = ex_b = NULL;slack = NULL;match = NULL;vis_a = vis_b = NULL;size = 0;diffSum = 0;
}KM::~KM()
{/* 释放之前分配了的内存 */if(ex_a){delete []ex_a;ex_a = NULL;}if(ex_b){delete []ex_b;ex_b = NULL;}if(vis_a){delete []vis_a;vis_a = NULL;}if(vis_b){delete []vis_b;vis_b = NULL;}if(slack){delete []slack;slack = NULL;}if(match){delete []match;match = NULL;}}void KM::init(int size, std::vector<std::vector<double>> diff)
{/* 获取差异度矩阵,根据矩阵的边长为其他变量分配空间 */this->diff = diff;this->size = size;ex_a = new double[size];ex_b = new double[size];vis_a = new bool[size];vis_b = new bool[size];slack = new double[size];match = new int[size];
}bool KM::dfs(int a)
{vis_a[a] = true;for(int b = 0; b < size; b++){if(vis_b[b])// 每一轮匹配中b类每个对象只尝试一次continue;double c = diff[a][b];double gap = ex_a[a] + ex_b[b] - c;// 差异度符合要求if(fabs(gap) < 1e-6){vis_b[b] = true;if(match[b] == -1 || dfs(match[b]))// 匹配成功{match[b] = a;return true;}}else{slack[b] = std::max(slack[b], gap);// 匹配失败,记录该b类对象若想成功匹配,其差异度减小的最小值}}return false;
}void KM::KM_Calc()
{for(int i = 0; i < size; i++){ex_b[i] = 0;// 每个b类对象的差异度初始为0match[i] = -1;ex_a[i] = (diff)[i][0];for(int j = 1; j < size; j++){ex_a[i] = std::min(ex_a[i], (diff)[i][j]);// 每个a类对象的初始期望值是与之相连的每个b类对象差异度的最小值}}for(int i = 0; i < size; i++){for(int m = 0; m < size; m++)slack[m] = -1000000.0;while(1){for(int m = 0; m <size; m++){vis_a[m] = false;vis_b[m] = false;}if(dfs(i))break;// 匹配成功,退出// 匹配失败double d = -1000000.0;for(int j = 0; j < size; j++){if(!vis_b[j])d = std::max(d, slack[j]);// 注意这里d小于0}for(int j = 0; j < size; j++){if(vis_a[j])ex_a[j] -= d;// 参加过匹配的a类对象只能提高差异度的期望值if(vis_b[j])ex_b[j] += d;// 参加过匹配的b类对象有资格“要求”a类对象降低差异度elseslack[j] -= d;}}}diffSum = 0;// 计算匹配结果的总差异度for(int i = 0; i < size; i++){diffSum += (diff)[match[i]][i];}
}int* KM::getMatch()
{return match;
}double KM::getdiffSum()
{return diffSum;
}

3)main.cpp提供调用示例,其中的数据来自http://blog.csdn.net/kevinjqy/article/details/54584114

#include <iostream>#include "km.h"int main()
{/* 构造差异度矩阵 */std::vector<std::vector<double>> diff;diff.resize(4);for(int i = 0; i < diff.size(); i++){diff[i].resize(4);}diff[0][0] = 90;diff[0][1] = 75;diff[0][2] = 75;diff[0][3] = 80;diff[1][0] = 35;diff[1][1] = 85;diff[1][2] = 55;diff[1][3] = 65;diff[2][0] = 125;diff[2][1] = 95;diff[2][2] = 90;diff[2][3] = 105;diff[3][0] = 45;diff[3][1] = 110;diff[3][2] = 95;diff[3][3] = 115;/* 初始化算法对象 */KM km;km.init(diff.size(), diff);/* 计算结果并输出 */km.KM_Calc();std::cout<<"diffSum: "<<km.getdiffSum()<<std::endl;int* match = km.getMatch();for(int i = 0; i < diff.size(); i++){std::cout<<match[i]<<" ";}std::cout<<std::endl;return 0;
}

参考文章:

[1] http://www.cnblogs.com/wenruo/p/5264235.html KM算法详解+模板

[2] http://blog.csdn.net/kevinjqy/article/details/54584114 分配问题与匈牙利算法

[3] http://blog.csdn.net/dark_scope/article/details/8880547 趣写算法系列之匈牙利算法

KM算法的实现和封装相关推荐

  1. BF算法及KMP算法的实现

    目录 前言 一.BF算法 1.BF算法是什么 2.BF算法的实现 二.KMP算法 1.KMP算法是什么 2.next数组 3.代码实现 总结 前言 例如:随着我们对字符串的不断学习和深入了解,我们会面 ...

  2. 素数判定 Miller-Rabin 算法的实现 python

    素数判定 Miller-Rabin 算法的实现 实验目的 通过实验掌握 Miller-Rabin 素数判定的算法. 实验原理 Miller-Rabin primality test | encyclo ...

  3. TD-SCDMA与TD-LTE异构网络垂直切换算法的实现

    TD-SCDMA与TD-LTE异构网络垂直切换算法的实现 在当前的移动通信领域中,TD-SCDMA和TD-LTE成为了两个主流的移动通信标准.在实际应用中,有时需要在这两种网络之间进行垂直切换以保证通 ...

  4. 约瑟夫问题算法的实现(代码实现) [Java][数据结构]

    约瑟夫问题算法的实现(代码实现) 代码如下(我们将这个方法也定义到单向环形链表类中): 其实我们的约瑟夫问题算法就相当于是一个特殊的删除单向环形链表中的结点的方法,所以我们就写到单向环形链表类中 /* ...

  5. 计算机图形学 区域填充,计算机图形学 区域填充算法的实现

    . '. 实验四区域填充算法的实现班级 08信计学号 58 姓名陈瑞雪分数 一.实验目的和要求: 1.掌握区域填充算法基本知识 2.理解区域的表示和类型,能正确区分四连通和八连通的区域 3.了解区域填 ...

  6. OpenCV中图像旋转(warpAffine)算法的实现过程

    在OpenCV中,目前并没有现成的函数直接用来实现图像旋转,它是用仿射变换函数cv::warpAffine来实现的,此函数目前支持4种插值算法,最近邻.双线性.双三次.兰索斯插值,如果传进去的参数为基 ...

  7. JAVA实现中点画线_实验1-中点画线和Bresenham画线算法的实现

    <实验1-中点画线和Bresenham画线算法的实现>由会员分享,可在线阅读,更多相关<实验1-中点画线和Bresenham画线算法的实现(9页珍藏版)>请在人人文库网上搜索. ...

  8. python边缘检测代码_python Canny边缘检测算法的实现

    图像边缘信息主要集中在高频段,通常说图像锐化或检测边缘,实质就是高频滤波.我们知道微分运算是求信号的变化率,具有加强高频分量的作用.在空域运算中来说,对图像的锐化就是计算微分.对于数字图像的离散信号, ...

  9. 干货回顾丨TensorFlow四种Cross Entropy算法的实现和应用

    交叉熵介绍 交叉熵(Cross Entropy)是Loss函数的一种(也称为损失函数或代价函数),用于描述模型预测值与真实值的差距大小,常见的Loss函数就是均方平方差(Mean Squared Er ...

最新文章

  1. leetcode-55 跳跃游戏
  2. php getconfig,PHP: tidy::getConfig - Manual
  3. java script 技巧
  4. 0227windows下模糊查询oracle事件的脚本
  5. vant按需引入没样式_传统背景墙早看腻了,不如走一圈石膏线简单好看,8种样式随意选...
  6. Adobe illustrator 调整对象大小 - 连载 11
  7. 零基础到进阶,Python正则表达式详解
  8. 事物注解方式: @Transactional
  9. 吴恩达机器学习作业Python实现(六):SVM支持向量机
  10. 应届毕业生如何打破面试焦虑
  11. VMWare安装配置Win7详解
  12. 游戏思考系列02:技能伤害计算流程(不涉及buff)
  13. 现在有一个未分库分表的系统,未来要分库分表,如何设计才可以让系统从未分库分表动态切换到分库分表上?
  14. 保姆级教学如何在Ubuntu 20.04工作站上配置深度学习环境
  15. 汉罗塔递归算法(C++)
  16. 半导体器件物理【5】固体量子 —— 能带与k空间
  17. 使用boostrap制作导航栏
  18. 微软新冠病毒感染员工的自白
  19. openSession和getCurrentSession
  20. 北京故宫博物院钟表馆改陈完成 以新面貌与观众见面

热门文章

  1. 28、Intel RealSense Depth Camera D415相机使用教程
  2. 【SpringMVC】SpringMVC的入门程序——HelloWorld(有点详细)
  3. RFS的web自动化验收测试——Selenium2Library升级指南(补充一个问题)
  4. 流利阅读day2 Anti-vaccination
  5. 小汽车托运收费标准?汽车托运价格如何核算
  6. udp发送的数据存入MySQL_怎么把收到的UDP数据批量存储到数据库
  7. 《摔跤吧!爸爸》中引人深思的中英文台词
  8. CNC加工中心几种分中方法
  9. 主板供电异常,USB/网线插入/重启导致电脑大概率蓝屏
  10. python abs函数能否求复数模_Python3 abs() 函数