一般图最大匹配——带花树
所謂花,就是如下圖所示的一個(gè)奇環(huán):
本文中粗邊代表現(xiàn)在的匹配邊,細(xì)邊代表該點(diǎn)的前驅(qū)(后文會(huì)講解前驅(qū)是什么,現(xiàn)在只需要知道每個(gè)點(diǎn)和它的前驅(qū)在原圖中一定是有邊的)。
如圖所示,一朵包含\(2k+1\)個(gè)點(diǎn)的花一定至多包含\(k\)條匹配邊,于是總會(huì)剩下一個(gè)未匹配的點(diǎn),上圖中即為\(1\)號(hào)點(diǎn)。
那么我們可以發(fā)現(xiàn),如果有另外一個(gè)點(diǎn)想要與花中的某個(gè)點(diǎn)\(v\)匹配,那么有兩種情況:1、\(v\)是未匹配的點(diǎn)(即1號(hào)點(diǎn)),那么直接與\(v\)匹配即可。2、\(v\)是已經(jīng)匹配的點(diǎn),這時(shí)只要將花中的匹配狀況修改,使得\(v\)變成未匹配的那個(gè)點(diǎn)即可。
綜上所述,只要花中的點(diǎn)沒(méi)有向外匹配,我們總是可以使得外部的一個(gè)點(diǎn)和花中任意一個(gè)點(diǎn)匹配,因此花的性質(zhì)和點(diǎn)其實(shí)很相似。我們將花縮成一個(gè)點(diǎn)來(lái)處理,就可以解決出現(xiàn)奇環(huán)的問(wèn)題。以上思想就是帶花樹(shù)算法的核心。
==================總之分割一下好了==================
帶花樹(shù)算法的過(guò)程其實(shí)和\(bfs\)版本的匈牙利是很相似的,都是找出一個(gè)交錯(cuò)樹(shù),交錯(cuò)樹(shù)可能長(zhǎng)這樣(注意每個(gè)藍(lán)色點(diǎn)可能有多個(gè)橙色兒子,但是每個(gè)橙色點(diǎn)只能有一個(gè)藍(lán)色兒子):
其中1號(hào)點(diǎn)就是我們嘗試增廣的節(jié)點(diǎn),在這里我們給每一個(gè)節(jié)點(diǎn)一個(gè)\(type\)值,若該點(diǎn)不在交錯(cuò)樹(shù)中,它的\(type\)值為\(0\),否則為\(1\)或\(2\)。上圖中我們用藍(lán)色點(diǎn)代表\(type=1\)的點(diǎn),橙色點(diǎn)代表\(type=2\)的點(diǎn),不難看出\(type\)值的不同其實(shí)代表了一種類(lèi)似于二分圖的關(guān)系,每個(gè)點(diǎn)在交錯(cuò)樹(shù)中只和\(type\)值不同的點(diǎn)相連。當(dāng)我們沒(méi)有找到奇環(huán)的時(shí)候,\(type\)值和二分圖是等價(jià)的。
那么仿照匈牙利的過(guò)程,我們將嘗試增廣的點(diǎn)\(v\)的\(type\)值設(shè)為\(1\)并開(kāi)始增廣,假設(shè)當(dāng)前處理的點(diǎn)為\(u\):
1、如果\(type_u=0\),就代表它不在交錯(cuò)樹(shù)中:
當(dāng)\(u\)已經(jīng)有匹配時(shí),我們就擴(kuò)展這棵交錯(cuò)樹(shù),將\(type_u\)的值設(shè)為\(2\)(因?yàn)槠浜?span id="ozvdkddzhkzd" class="math inline">\(type\)值為\(1\)的\(v\)相鄰),并將\(type_{match_u}\)的值設(shè)為\(1\)(同理)。這時(shí)我們就可以把\(match_u\)塞進(jìn)隊(duì)列里了,如果能夠沿著\(match_u\)找到增廣路的話(huà)我們就可以讓\(match_u\)匹配那個(gè)增廣的點(diǎn)并將\(u\)與\(v\)匹配,這樣就使匹配數(shù)增加了\(1\)。同時(shí)我們將\(u\)的前驅(qū)(用\(pre_u\)表示)設(shè)置為\(v\),這是為了方便在找到增廣路以后一路返回修改匹配。
當(dāng)\(u\)并沒(méi)有匹配時(shí),我們就成功找到了一條增廣路,此時(shí)沿著由\(pre\)和\(match\)連成的邊一路修改就增廣完成了,返回。
2、如果\(type_u=2\),代表你找到了一個(gè)偶環(huán),并沒(méi)有什么用,就跳過(guò)這個(gè)點(diǎn)。
3、這里是最重點(diǎn)的,如果\(type_u=1\),代表你找到了一個(gè)奇環(huán),這就代表你的\(type\)值不再等價(jià)于二分圖了,我們這個(gè)時(shí)候就可以開(kāi)始“縮花”操作,將我們找到的奇環(huán)縮成一個(gè)點(diǎn)。讓我們具體的考慮一下:
首先,快速找到哪些點(diǎn)在這個(gè)奇環(huán)中,顯然\(u\)和\(v\)一定都出現(xiàn)在交錯(cuò)樹(shù)上(\(type\)不為\(0\)),結(jié)合上面的那張圖考慮,奇環(huán)的范圍就是兩個(gè)點(diǎn)在交錯(cuò)樹(shù)上的鏈中包含的所有點(diǎn),因此我們需要找到這兩個(gè)點(diǎn)的\(lca\),這里直接采用暴力向上跳的做法即可。
找到以后怎么連接\(pre\)邊呢?我們參考一下文章開(kāi)頭的結(jié)構(gòu),可以發(fā)現(xiàn)此時(shí)的\(lca\)一定就是那個(gè)花中唯一的沒(méi)有匹配或者匹配到外面節(jié)點(diǎn)的\(1\)號(hào)點(diǎn)。因此感性思考一下,我們應(yīng)該“誘導(dǎo)”其他所有的點(diǎn)通過(guò)\(pre\)和\(match\)邊一路走走到\(lca\)上,因此將除了與\(lca\)相連的\(pre\)邊外其他的\(pre\)邊都改為雙向邊,并將\(u\)和\(v\)也改成雙向邊即可達(dá)到這個(gè)目的。
最后做一點(diǎn)掃尾工作,我們可以通過(guò)并查集將奇環(huán)縮成一個(gè)點(diǎn)(因此在普通增廣的時(shí)候也要考慮并查集的情況),不妨用\(lca\)做這朵花的代表。同時(shí)再考慮一下這個(gè)新點(diǎn)的\(type\)值應(yīng)該設(shè)為什么,因?yàn)槊總€(gè)橙色點(diǎn)最多只有一個(gè)兒子(它的匹配點(diǎn)),因此\(lca\)一定是藍(lán)色點(diǎn),因此新點(diǎn)的\(type\)值為\(1\),所以要注意將花中所有\(type\)值為\(2\)的點(diǎn)修改為\(1\)并且加入隊(duì)列。
當(dāng)我們找不到新的點(diǎn)時(shí),本次增廣失敗。
===============分割分割================
以上就是一般圖最大匹配的增廣過(guò)程,注意在每次增廣之前,\(pre\),\(type\)以及并查集都是要初始化的。將并查集的復(fù)雜度看作常數(shù),則每次增廣至多是\(O(m)\)的,一共需要增廣\(O(n)\)次,因此帶花樹(shù)算法的復(fù)雜度和匈牙利一樣,都是\(O(nm)\),當(dāng)然,在實(shí)踐中帶花樹(shù)算法跑得一般會(huì)比理論上界快很多。
相信在熟練理解帶花樹(shù)的過(guò)程后一定能寫(xiě)出代碼,因此為了不對(duì)讀者造成思維定式影響這里就不貼代碼了。完結(jié)撒花~
轉(zhuǎn)載于:https://www.cnblogs.com/Mr-Spade/p/9636874.html
總結(jié)
以上是生活随笔為你收集整理的一般图最大匹配——带花树的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: kibana从入门到精通-Kibana安
- 下一篇: (二)流--递归算法