奔小康赚大钱 HDU - 2255( 二分图匹配KM算法详解)

题目

传说在遥远的地方有一个非常富裕的村落,有一天,村长决定进行制度改革:重新分配房子。 
这可是一件大事,关系到人民的住房问题啊。村里共有n间房间,刚好有n家老百姓,考虑到每家都要有房住(如果有老百姓没房子住的话,容易引起不安定因素),每家必须分配到一间房子且只能得到一间房子。 
另一方面,村长和另外的村领导希望得到最大的效益,这样村里的机构才会有钱.由于老百姓都比较富裕,他们都能对每一间房子在他们的经济范围内出一定的价格,比如有3间房子,一家老百姓可以对第一间出10万,对第2间出2万,对第3间出20万.(当然是在他们的经济范围内).现在这个问题就是村领导怎样分配房子才能使收入最大.(村民即使有钱购买一间房子但不一定能买到,要看村领导分配的). 

Input

输入数据包含多组测试用例,每组数据的第一行输入n,表示房子的数量(也是老百姓家的数量),接下来有n行,每行n个数表示第i个村名对第j间房出的价格(n<=300)。 

Output

请对每组数据输出最大的收入值,每组的输出占一行。 
 

Sample Input

2
100 10
15 23

Sample Output

123

分析:

KM模板,具体讲解如下:

AC代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int inf=0x3f3f3f3f;
const int M=330;
int mp[M][M];
int s[M];
int a[M],b[M];
int u[M],v[M];
int n,ans;
int dp[M];
int x,y;
int dfs(int o)
{u[o]=1;for(int g=1;g<=y;g++){if(v[g])continue;int tmp=a[o]+b[g]-mp[o][g];if(tmp==0){v[g]=1;if(dp[g]==-1||dfs(dp[g])){dp[g]=o;return 1;}}else if(s[g]>tmp)s[g]=tmp;}return 0;
}
void KM()
{memset(a,0,sizeof(a));memset(b,0,sizeof(b));for(int i=1;i<=x;i++)for(int j=1;j<=y;j++)a[i]=max(a[i],mp[i][j]);for(int k=1;k<=x;k++){for(int i=1;i<=y;i++)s[i]=inf;while(1){memset(u,0,sizeof(u));memset(v,0,sizeof(v));if(dfs(k)) break;int num=inf;for(int i=1;i<=y;i++)if(!v[i]&&num>s[i])num=s[i];for(int i=1;i<=x;i++)if(u[i])a[i]-=num;for(int i=1;i<=y;i++){if(v[i])b[i]+=num;else s[i]-=num;}}}
}
int main()
{while(~scanf("%d",&n)){ans=0;x=y=n;memset(dp,-1,sizeof(dp));for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)scanf("%d",&mp[i][j]);KM();for(int i=1;i<=y;i++)ans+=mp[dp[i]][i];printf("%d\n",ans);}return 0;
}

笔者刷数模题的时候有一道题考到了“二分图最大权分配”,需要用到KM算法,但是书上对KM算法的介绍又臭又长,更何况有些同学“匈牙利算法”也没学过(由匈牙利数学家Edmonds提出),自然难以理解所谓的KM算法。本文旨在用通俗易懂的语言,向读者介绍匈牙利算法和KM算法。

 

一、匈牙利算法

匈牙利算法用于解决什么问题?

匈牙利算法用于解决二分图的最大匹配问题。

什么是二分图?我们不妨来考虑这样一个问题,在一家公司里,有员工A,B,C,有三种工作a,b,c,如果员工和工作之间有线相连,则代表员工能胜任这份工作。

figure1.1

如图所示,员工A能胜任a,c工作,员工B能胜任a,b,c工作,而员工C只能胜任c工作。

上图就是所谓的“二分图”(请忽略图中箭头),简单的说,上图可划分为两个集合{员工},{工作},两个集合之间的元素可以相连,同一个集合内的元素不能相连。

下面请解决这样一个问题:请给出一个方案,让尽可能多的员工有不同的工作做。“匈牙利算法”的出现就是为了解决这个问题。

下面给出这个问题的解决方案:(读者看到这里可能会想,解决这个问题不是很简单吗?A→a,B→b,C→c不就好了?请注意:任何算法的给出都是为了规整化一个问题的解决步骤,其目的是为了在问题规模越来越大时算法对其仍然适用,如果公司有10个员工或者更多,读者想必不能一眼看出答案,此时,算法的作用便体现出来了。)

我们先帮A找工作做,不妨先帮A连上a(红线表示此时两者已经匹配),表示A去做a工作。

  

接下来我们帮B找工作做,B的第一条线跟a相连,B也想做工作a。

这时候就发生了冲突(collision),如何解决呢?

匈牙利算法”指出 通过一条增广路径,通过取反操作,我们就能匹配更多的点。

什么是增广路径?增广路径是指,由一个未匹配的顶点开始,经过若干个匹配顶点,最后到达对面集合的一个未匹配顶点的路径,即这条路径将两个不同集合的两个未匹配顶点通过一系列匹配顶点相连。

比如此题中,B想做工作a,于是A想着换一个工作,我们连上A,c。

 

如图,B→a→A→c 其中B,c未匹配,A,a已匹配,按照定义,这就是一条增广路径,我们对其进行取反操作,就变成了下图。

 

取反操作为我们带来了什么?原本只有一边匹配,现在有两边匹配了,而且冲突也解决了,这就是匈牙利算法的妙处,我们能通过不停的寻找这样一条增广路径,从而找到二分图的最大匹配

下面我们继续寻找增广路径。

最终,我们得到了一个最大匹配:A匹配a,B匹配b,C匹配c,就算集合中的元素很多很多,我们仍能通过匈牙利算法得到该二分图的最大匹配。

二、KM算法


现在我们来考虑另外一个问题:如果每个员工做每件工作的效率各不相同,我们如何得到一个最优匹配使得整个公司的工作效率最大呢?

这种问题被称为带权二分图的最优匹配问题,可由KM算法解决。

比如上图,A做工作a的效率为3,做工作c的效率为4......以此类推。

不了解KM算法的人如何解决这个问题?我们只需要用匈牙利算法找到所有的最大匹配,比较每个最大匹配的权重,再选出最大权重的最优匹配即可。这不失为一个解决方案,但是,如果公司员工的数量越来越多,此种算法的实行难度也就越来越大,我们必须另辟蹊径:KM算法。

KM算法解决此题的步骤如下所示:

1.首先对每个顶点赋值,将左边的顶点赋值为最大权重,右边的顶点赋值为0。

如图,我们将顶点A赋值为其两边中较大的4。

2.进行匹配,我们匹配的原则是:只与权重相同的边匹配,若是找不到边匹配,对此条路径的所有左边顶点-1,右边顶点+1,再进行匹配,若还是匹配不到,重复+1和-1操作。(这里看不懂可以跳过,直接看下面的操作,之后再回头来看这里。)

对A进行匹配,符合匹配条件的边只有Ac边。

匹配成功!

接下来我们对B进行匹配,顶点B值为3,Bc边权重为3,匹配成~ 等等,A已经匹配c了,发生了冲突,怎么办?我们这时候第一时间应该想到的是,让B换个工作,但根据匹配原则,只有Bc边 3+0=0 满足要求,于是B不能换边了,那A能不能换边呢?对A来说,也是只有Ac边满足4+0=4的要求,于是A也不能换边,走投无路了,怎么办?

从常识的角度思考:其实我们寻找最优匹配的过程,也就是帮每个员工找到他们工作效率最高的工作,但是,有些工作会冲突,比如现在,B员工和A员工工作c的效率都是最高,这时我们应该让A或者B换一份工作,但是这时候换工作的话我们只能换到降低总体效率值的工作,也就是说,如果令R=左边顶点所有值相加,若发生了冲突,则最终工作效率一定小于R,但是,我们现在只要求最优匹配,所以,如果A换一份工作降低的工作效率比较少的话,我们是能接受的(对B同样如此)。

在KM算法中如何体现呢?

现在参与到这个冲突的顶点是A,B和c,令所有左边顶点值-1,右边顶点值+1,即 A-1,B-1. c+1,结果如下图所示。

我们进行了上述操作后会发现,若是左边有n个顶点参与运算,则右边就有n-1个顶点参与运算,整体效率值下降了1*(n-(n-1))=1,而对于A来说,Ac本来为可匹配的边,现在仍为可匹配边(3+1=4),对于B来说,Bc本来为可匹配的边,现在仍为可匹配的边(2+1=3),我们通过上述操作,为A增加了一条可匹配的边Aa,为B增加了一条可匹配的边Ba。

现在我们再来匹配,对B来说,Ba边 2+0=2,满足条件,所以B换边,a现在为未匹配状态,Ba匹配!

我们现在匹配最后一条边C,Cc 5+1!=5,C边无边能匹配,所以C-1。

现在Cc边 4+1=5,可以匹配,但是c已匹配了,发生冲突,C此时不能换边,于是便去找A,对于A来说,Aa此时也为可匹配边,但是a已匹配,A又去找B。

  

B现在无边可以匹配了,2+0!=1 ,现在的路径是C→c→A→a→B,所以A-1,B-1,C-1,a+1,c+1。如下图所示。

对于B来说,现在Bb 1+0=1 可匹配!

使用匈牙利算法,对此条路径上的边取反。

如图,便完成了此题的最优匹配。

读者可以发现,这题中冲突一共发生了3次,所以我们一共降低了3次效率值,但是我们每次降低的效率值都是最少的,所以我们完成的仍然是最优匹配

这就是KM算法的整个过程,整体思路就是:每次都帮一个顶点匹配最大权重边,利用匈牙利算法完成最大匹配,最终我们完成的就是最优匹配

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

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

相关文章

记一次排查线上程序内存的忽高忽低,又是大集合惹祸了

一&#xff1a;背景1. 讲故事昨天继续还技术债&#xff0c;优化一轮后的程序拉到线上后内存继续忽高忽低&#xff0c;低的时候20G&#xff0c;高的时候30G&#xff0c;过了一会又下降了几个G&#xff0c;毫无疑问&#xff0c;程序中有什么集合或者什么操作占用了大量内存&#…

[C++11]forward完美转发

// 函数原型 template <class T> T&& forward (typename remove_reference<T>::type& t) noexcept; template <class T> T&& forward (typename remove_reference<T>::type&& t) noexcept;// 精简之后的样子 std::forward…

Pipe HDU - 2150(判断线段相交+向量叉乘线代详解)

题目&#xff1a; 经过激烈的争夺&#xff0c;Lele终于把那块地从Yueyue的手里抢了回来。接下来&#xff0c;Lele要开始建造他的灌溉系统。 通过咨询Lele的好友——化学系的TT&#xff0c;Lele决定在田里挖出N条沟渠&#xff0c;每条沟渠输送一种肥料。 每条沟渠可以看作是一…

win7如何将计算机移至桌面,如何将win7电脑桌面的文件转移到其他盘中?

想必很多朋友都和小编一样&#xff0c;是一个嫌麻烦的人&#xff0c;是一个不怎么爱收拾的人吧?这种人有一个通病&#xff0c;那就是喜欢将一些重要的文件放置在win7 64位纯净版下载的桌面上&#xff0c;这样的话&#xff0c;不仅容易找到&#xff0c;而且方便使用&#xff0c…

凸包算法知识总结

首先&#xff0c;什么是凸包&#xff1f; 假设平面上有p0~p12共13个点&#xff0c;过某些点作一个多边形&#xff0c;使这个多边形能把所有点都“包”起来。当这个多边形是凸多边形的时候&#xff0c;我们就叫它“凸包”。 处理何种问题&#xff1a;凸包可以看成在木板上钉许多…

[C++11]shared_ptr使用的注意事项(内存被重复析构,内存泄漏问题)

shared_ptr使用的注意事项: 1.不能使用一个原始地址初始化多个共享智能指针 2.函数不能返回管理了this的共享智能指针对象 3.共享智能指针不能循环引用 不能使用一个原始地址初始化多个共享智能指针 代码如下: #include <iostream> #include <memory> using name…

一文解读使用WinDbg排查iis 中CPU占用高的站点问题

一、概述在Window服务器部署程序后&#xff0c;可能因为代码的不合理或者其他各种各样的问题&#xff0c;会导致CPU暴增&#xff0c;甚至达到100%等情况&#xff0c;严重危及到服务器的稳定以及系统稳定&#xff0c;但是一般来说对于已发布的程序&#xff0c;没法即时看到出问题…

Power Network POJ - 1459(EK算法模板+详解)

题意&#xff1a; 总共有a个节点&#xff0c;其中有发电站b个、用户c个和调度器a-b-c个三种节点&#xff0c;每个发电站有一个最大发电量&#xff0c;每个用户有个最大接受电量&#xff0c;现在有d条有向边&#xff0c;边有一个最大的流量代表&#xff0c;最多可以流出这么多电…

[C++11]shared_ptr共享智能指针的初始化与使用

使用智能指针需要添加头文件: 代码如下: #include <memory>shared_ptr使用的注意事项: 1.不能使用一个原始地址初始化多个共享智能指针 2.函数不能返回管理了this的共享智能指针对象 3.共享智能指针不能循环引用 初始化: 1.通过构造函数初始化 代码如下: std::shar…

人在职场,表达似水

最近几天集中在和部门内的小伙伴做1&#xff1a;1Talk。发现有不少人表达能力真的不是很好&#xff0c;而且水平长期停滞不前。现场与他们分享了一些我的经验&#xff0c;趁热打铁也想在这里分享给大家。表达能力不好本身就是做技术的人身上的一个常见标签。如果仅仅是标签也就…

[C++11]弱引用智能指针weak_ptr初始化和相关的操作函数

弱引用智能指针 std::weak_ptr 可以看做是 shared_ptr 的助手&#xff0c;它不管理 shared_ptr 内部的指针。std::weak_ptr 没有重载操作符 * 和 ->&#xff0c;因为它不共享指针&#xff0c;不能操作资源&#xff0c;所以它的构造不会增加引用计数&#xff0c;析构也不会减…

电子科技大学计算机网络技术专业,计算机专业前景如何?最强十大高校有哪些?电子科大排名多少?...

近几年计算机专业以及与计算机相关的专业可谓是火的不能再火&#xff0c;越来越多的考生在高考时选择了计算机专业或者与计算机相关的专业&#xff01;事实也是如此&#xff0c;随着科技的进步&#xff0c;信息技术的迅猛发展&#xff0c;特别是计算机技术的发展&#xff0c;以…

写一个简版 asp.net core

动手写一个简版 asp.net coreIntro之前看到过蒋金楠老师的一篇 200 行代码带你了解 asp.net core 框架&#xff0c;最近参考蒋老师和 Edison 的文章和代码&#xff0c;结合自己对 asp.net core 的理解 &#xff0c;最近自己写了一个 MiniAspNetCore &#xff0c;写篇文章总结一…

[C++11]独占的智能指针unique_ptr的删除器

unique_ptr 指定删除器和 shared_ptr 指定删除器是有区别的&#xff0c;unique_ptr 指定删除器的时候需要确定删除器的类型&#xff0c;所以不能像 shared_ptr 那样直接指定删除器&#xff0c;举例说明&#xff1a; 代码如下: #include <iostream> #include <memory…

如何在Windows上使用Git创建一个可执行脚本?

长话短说&#xff0c;今天介绍如何在windows上使用Git上创建一个可执行的shell脚本。“首先我们要知道windows上Git默认添加的文件权限是:-rw-r--r--(对应权限值是644)&#xff0c;而通常创建的shell脚本都希望天然可执行&#xff0c;故有必要在Windows上使用Git管理shell脚本时…

[C++11]共享智能指针shared_ptr指定删除器

当智能指针管理的内存对应的引用计数变为 0 的时候&#xff0c;这块内存就会被智能指针析构掉了。另外&#xff0c;我们在初始化智能指针的时候也可以自己指定删除动作&#xff0c;这个删除操作对应的函数被称之为删除器&#xff0c;这个删除器函数本质是一个回调函数&#xff…

基于 abp vNext 和 .NET Core 开发博客项目 - 再说Swagger,分组、描述、小绿锁

在开始本篇正文之前&#xff0c;解决一个 疯疯过 指出的错误&#xff0c;再次感谢指正。步骤如下&#xff1a;删掉.Domain.Shared层中的项目引用&#xff0c;添加nuget依赖包Volo.Abp.Identity.Domain.Shared&#xff0c;可以使用命令&#xff1a;Install-Package Volo.Abp.Ide…

[C++11]独占的智能指针unique_ptr的初始化和使用

std::unique_ptr 是一个独占型的智能指针&#xff0c;它不允许其他的智能指针共享其内部的指针&#xff0c;可以通过它的构造函数初始化一个独占智能指针对象&#xff0c;但是不允许通过赋值将一个 unique_ptr 赋值给另一个 unique_ptr。std::unique_ptr 不允许复制&#xff0c…

Abp v2.8.0发布 路线图

ABP框架和ABP商业版v2.8已经发布.这篇文章将涵盖这些发布中的新增内容和项目的中期路线图.ABP框架2.8有哪些新增内容?你可在GitHub的发行说明中看到所有的变更.这篇博客只包括重要的一些功能/变更.SignalR集成包我们已经发布了一个新的包用来集成SignalR到基于ABP框架应用程序…