匈牙利算法一般应用于二分图的匹配问题。
大家可以在网上搜索一篇匈牙利算法之找妹子的文章,个人认为他写得很精彩~
算法如下:
bool find(int a) { for (int i = 1; i <= n2; i++) { if (data[a][i] == 1 && !state[i]) { //如果节点i与a相邻并且未被查找过 state[i] = true; //标记i为已查找过 if (result[i] == 0 //如果i未在前一个匹配M中 || find(result[i])) { //i在匹配M中,但是从与i相邻的节点出发可以有增广路 result[i] = a; //记录查找成功记录 return true; //返回查找成功 } } } return false; } int main() { for (int i = 1; i <= n1; i++) { memset(state,0,sizeof(state)); //清空上次搜索时的标记 if (find(i)) ans++; //从节点i尝试扩展 } printf("%d\n",ans); return 0; }
二分图
无向图中顶点可分为两个不相交集合X和Y,使得图中每一条边都分别连接X、Y中的顶点。
同一顶点集合内部无边。
二分图的判定
染色法:
对任一未染色顶点染色;
若其相邻顶点未染色,染另一种色;
若相邻顶点已染不同色,继续;
若相邻顶点已染同一色,不是二分图,终止;
使用BFS/DFS重复上述过程。
任何无向无环图均为二分图,如树。
增广路径
从一个未匹配点出发,依次交替经过非匹配边、匹配边、非匹配边…,若途经另一个未匹配点,则该路径称为增广路径。
增广路径的特性
有奇数条边,且非匹配边比匹配边多一条。
起点和终点为未匹配点,其他均为匹配点。
所有第奇数条边都不在原匹配中,所有第偶数条边恰相反。
增广路径取反:
若将所有第偶数条边从原匹配移除,并加入所有第奇数条边,则可使匹配数 + 1.
思路
寻找增广路径,并通过增广路径取反增加匹配数。
不断进行,直到无法找到新的增广路径。
初始时,任一条边均可做增广路径。
不同的顺序可能导致不同的匹配,但最终匹配数相同(最大)。
增广路径的寻找
从一个未匹配点出发,DFS或BFS均可,到达其他未匹配点即终止。
限制:若经过某个匹配点,则接下来必须经过其匹配边。
保证未匹配边、匹配边依次交替。
BFS对于大型稀疏图性能更高,但DFS代码简洁,不易出错。
bool 寻找从k出发的对应项出的可增广路 { while (从邻接表中列举k能关联到顶点j) { if (j不在增广路上) { 把j加入增广路; if (j是未盖点 或者 从j的对应项出发有可增广路) { 修改j的对应项为k; 则从k的对应项出有可增广路,返回true; } } } 则从k的对应项出没有可增广路,返回false; } void 匈牙利hungary() { for i->1 to n { if (则从i的对应项出有可增广路) 匹配数++; } 输出 匹配数; }