您的位置:首页 > 其它

【学会就脱单算法】匈牙利算法之飞行员配对问题(二分图最大匹配)

2018-04-05 15:17 441 查看
hello, everyone, I'm Arabic1666!             

    首先看题如下,看懂问题才能解决问题
飞行员配对(二分图最大匹配) 题目来源: 网络流24题基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题第二次世界大战时期,英国皇家空军从沦陷国征募了大量外籍飞行员。由皇家空军派出的每一架飞机都需要配备在航行技能和语言上能互相配合的2名飞行员,其中1名是英国
4000
飞行员,另1名是外籍飞行员。在众多的飞行员中,每一名外籍飞行员都可以与其他若干名英国飞行员很好地配合。如何选择配对飞行的飞行员才能使一次派出最多的飞机。对于给定的外籍飞行员与英国飞行员的配合情况,编程找出一个最佳飞行员配对方案, 使皇家空军一次能派出最多的飞机。 Input
第1行有2个正整数 m 和 n。n 是皇家空军的飞行 员总数(n<100);m 是外籍飞行员数。外籍飞行员编号为 1~m;英国飞行员编号为 m+1~n。接下来每行有 2 个正整数 i 和 j,表示外籍飞行员 i 可以和英国飞行员 j 配合。输入最后以 2 个-1 结束。
Output
第 1 行是最佳飞行员配对方案一次能派出的最多的飞机数 M。如果所求的最佳飞行员配对方案不存在,则输出‘No Solution!’。
Input示例
5 10
1 7
1 8
2 6
2 9
2 10
3 7
3 8
4 7
4 8
5 10
-1 -1
Output示例
4


        这是昨天刷基础题的时候,碰到的一道网络流的题,首先是自己想解决思路:当然是暴力回溯来获得最大匹配数(表示只能过三个测试点,思索许久 莫名悲伤 只得默默百度一下),发现这是网络流的的一道模板题(不得不感叹和大神们还是遥遥不可及啊),    网络题有很多解法:Dinic大法、Hungary大法(匈牙利算法 注:不是hungry,英语不好的我要强调一下警醒一下我自己)......        这里我只看了匈牙利算法,因为跟我自己的思路有点相似,只不过回溯做了优化,But 结果却是天壤之别(堪称点睛之笔,大家一定要看完,学习算法固好 更重要的是学习大神的思维)。        Ok!不扯犊子了,我们来学习一下匈牙利数学家Edmonds提出的这个匈牙利算法。为了让更多的人看懂文章,轻松入门像我一样,我决定用故事(改编的)来描述该算法的思路。(匈牙利算法是基于Hall定理中充分性证明的思想,它是二分图匹配最常见的算法,该算法的核心就是寻找增广路径,它是一种用增广路径求二分图最大匹配的算法。------摘自百度百科)看完介绍是不是异常懵逼,不用担心,我也是的。^_^

=========================故事开始==============================    2018年农历七月初七前一天,某IT公司决定举办一场相亲大会!这可是程序员的福利啊,有这样的公司我一定要去 麻蛋!另外相亲牵手成功的男女可以在七夕节休息一天!一大波单身员工欣喜若狂,表示吃够了狗粮,不耐寂寞,参与到了该次大会。
大会方案:1.首先把单身狗们分为:单男、单女们两大类就是二分图:如果把人看成点牵手则看成边,那么这里一个图就被分成了两部分,相同的部分的点彼此没有边----假设性取向都正常 不是gay 或 les,那这个图就是二分图G,二分图是特殊的图);2.现在每个人打开恋爱APP,标选出自己心动有好感的对象们(每个人都可以对多名异性有好感,也可以都没好感,直接滚---送女朋友都不要)3.大会组织者也就是月老, 只对彼此有好感的(彼此通过APP标记了对方的)一对男女进行撮合(单相思的大哥大姐们对不住了,不能强人所难是不 -_- ),  秉持着不惜一切代价撮合最多对的原则在二分图G中,一对匹配成功的情侣即可叫做一个匹配。牵手成功的情侣达到最大对数也就称之为最大匹配数,因此该类型问题也就叫做最大匹配问题),  开始牵线! 对他她们两大类群分别进行编号。 然后有好感的就先用红线标记起来。
你拥有的大概就是下面这样一张关系图,每一条连线都表示互有好感。(假设目前参加大会的是四男四女单身狗)


本着救人一命,胜造七级浮屠的原则,必须不惜一切代价撮合最多对的情侣,匈牙利算法会教你这样做:
=========================步 骤=================================1st: 先试着给1号男生找妹子,发现第一个和他相连的1号女生还名花无主,got it,连上一条蓝线(匹配数为1,表示这一对正在交往,当然很有可能被取代 对于好看又有才的人儿 竞争那是相当激烈的啊)


=============================================================2nd:1号男同胞正在尝试与一号女生交往,我们先不管他们俩,现在给2号男生找交往对象,发现第一个和他相连的2号女生名花无主,got it(当前匹配数为2)


=============================================================3rd:接下来是3号男生,很遗憾1号女生正在与1号男生交往呐,这该如何是好呢?哈哈男3长鼻子还是很聪明的,将情况上报给了公司,公司发现1号男生魅力很高 有其他彼此有好感女生,于是决定试着给 1号男生 另外分配一个妹子尝试交往。 现在开始商讨能否分配成功...(黄色表示这条边被临时拆掉)


与1号男生相连红线的第二个女生是2号女生,但是2号女生也有主了,怎么办呢?我们又再试着给2号女生的原配(2号男生)重新找个妹子吧(注意这个步骤和上面是一样的,这是一个递归的过程---此乃妙笔)



此时发现2号男生还能找到3号女生,那么之前商讨的问题就迎刃而解了,回溯回去2号男生可以找3号妹子~~~        1号男生可以找2号妹子了~~~       3号男生可以找1号妹子 end

 

    

所以第三步最后的结果就是(当前匹配数为3):


=============================================================4th: 接下来是4号男生,非常遗憾的是,即使按照第三步的节奏我们也没法给4号男生腾出来一个妹子来。如果强行给4号男生腾出来一个妹子的话,按照第三步的逻辑势必1~3号男生中有一个男生没女朋友了,这样做是徒劳的(因为当前匹配数还是3 没有变),于是乎,公司宣布:“香吉士同学,请您不要悲伤,为表以同情与鼓励,特别奖励你七夕节加班工资翻番!"=============================================================
OK!相亲大会至此结束。这也就是匈牙利算法的整个流程。其中最主要的就是找妹子是个递归的过程,最最关键的就是如何去“腾”。(注:上述算法讲解以及图片参考自:dark_scope)
基本原则大概是:有机会就上,没机会创造机会也要上愿有情人终成眷属!
=========================解决问题==============================
回到飞行员配对这个问题上来,一共有n位飞行员,有m位外籍佬和n-m位英国佬。也就是说参加相亲的单身狗有n个人,其中我们可以把外籍佬看成男生英国佬看成女生。然后按编号来看


这样我们就可以从匈牙利算法第一步开始工作:依次给1~5号男生匹配女朋友啦~上代码    #include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>

#define NMAX 105
using namespace std;
struct PAIR{
int girl[NMAX] = {0}; //该男生心仪女孩编号
int len = 0; //该男生心仪女孩个数
bool operator < (const PAIR other) const{
return this->len < other.len;
}
}target[NMAX]; //保存上图的男女关系
int m, n, res = 0, ans = 0;
int boyfriend[NMAX]; //指向男票id
bool isSingle[NMAX]; //单身即机会

bool find_girlfriend(int boy){
for (int j = 0; j < target[boy].len; ++j) { //遍历所有该boy的心仪目标 id 从0开始
int t = target[boy].girl[j]; //方便书写啦,当时用的结构体for表达更形象 懒得改呐
if( isSingle[t] ) { //该女孩单着的 有机会 上!
isSingle[t] = false;
if (boyfriend[t] == -1 || find_girlfriend( boyfriend[t]) ) {
//女孩t还没男票 or t现任很花心,有其他红线且和其他某暧昧女生匹配成功,也就变成了前任
boyfriend[t] = boy;
return true; //算是牵手成功咯
}
}
}
return false;
}

int main() {
int i, j, maxMatch = 0;
cin>>m>>n; //m外籍(男生数) n为所有(男女总数)
memset(boyfriend, -1 , sizeof(boyfriend)); // -1 means no match any boy.
while (cin >> i >> j) { // i:man j:woman
if (i == -1 && j == -1) {
break;
}
target[i].girl[ target[i].len++ ] = j;
}
sort(target+1,target+m+1); //专一的男生先匹配
// for (int k = 1; k <= m; ++k) {
// for (int l = 0; l < target[k].len; ++l) {
// cout<<target[k].a[l]<<" ";
// }
// cout<<endl;
// }
for (int k = 1; k <= m; ++k) {    //男生id从1开始 to match girl.
memset(isSingle, true , sizeof(isSingle)); //先撇清关系 Maybe have a better boy for waitting, like u?
if ( find_girlfriend(k) )
maxMatch++;    //当前最大匹配数
}
if(maxMatch==0){
cout<<"No solution!"<<endl;
} else
cout<<maxMatch<<endl;
return 0;
}    好了,一个上午过去了,终于算是over了。
    希望这篇文章可以帮到你,解除你心中的疑惑,如果有任何疑问都可以留言哦。谢谢!
    ===(*^▽^*)===
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: