所謂花,就是以下圖所示的一個奇環: 算法
本文中粗邊表明如今的匹配邊,細邊表明該點的前驅(後文會講解前驅是什麼,如今只須要知道每一個點和它的前驅在原圖中必定是有邊的)。blog
如圖所示,一朵包含$2k+1$個點的花必定至多包含$k$條匹配邊,因而總會剩下一個未匹配的點,上圖中即爲$1$號點。隊列
那麼咱們能夠發現,若是有另一個點想要與花中的某個點$v$匹配,那麼有兩種狀況:一、$v$是未匹配的點(即1號點),那麼直接與$v$匹配便可。二、$v$是已經匹配的點,這時只要將花中的匹配情況修改,使得$v$變成未匹配的那個點便可。class
綜上所述,只要花中的點沒有向外匹配,咱們老是可使得外部的一個點和花中任意一個點匹配,所以花的性質和點其實很類似。咱們將花縮成一個點來處理,就能夠解決出現奇環的問題。以上思想就是帶花樹算法的核心。擴展
==================總之分割一下好了==================im
帶花樹算法的過程其實和$bfs$版本的匈牙利是很類似的,都是找出一個交錯樹,交錯樹可能長這樣(注意每一個藍色點可能有多個橙色兒子,可是每一個橙色點只能有一個藍色兒子): img
其中1號點就是咱們嘗試增廣的節點,在這裏咱們給每個節點一個$type$值,若該點不在交錯樹中,它的$type$值爲$0$,不然爲$1$或$2$。上圖中咱們用藍色點表明$type=1$的點,橙色點表明$type=2$的點,不難看出$type$值的不一樣其實表明了一種相似於二分圖的關係,每一個點在交錯樹中只和$type$值不一樣的點相連。當咱們沒有找到奇環的時候,$type$值和二分圖是等價的。di
那麼仿照匈牙利的過程,咱們將嘗試增廣的點$v$的$type$值設爲$1$並開始增廣,假設當前處理的點爲$u$:思考
一、若是$type_u=0$,就表明它不在交錯樹中:co
當$u$已經有匹配時,咱們就擴展這棵交錯樹,將$type_u$的值設爲$2$(由於其和$type$值爲$1$的$v$相鄰),並將$type_{match_u}$的值設爲$1$(同理)。這時咱們就能夠把$match_u$塞進隊列裏了,若是可以沿着$match_u$找到增廣路的話咱們就可讓$match_u$匹配那個增廣的點並將$u$與$v$匹配,這樣就使匹配數增長了$1$。同時咱們將$u$的前驅(用$pre_u$表示)設置爲$v$,這是爲了方便在找到增廣路之後一路返回修改匹配。
當$u$並無匹配時,咱們就成功找到了一條增廣路,此時沿着由$pre$和$match$連成的邊一路修改就增廣完成了,返回。
二、若是$type_u=2$,表明你找到了一個偶環,並無什麼用,就跳過這個點。
三、這裏是最重點的,若是$type_u=1$,表明你找到了一個奇環,這就表明你的$type$值再也不等價於二分圖了,咱們這個時候就能夠開始「縮花」操做,將咱們找到的奇環縮成一個點。讓咱們具體的考慮一下:
首先,快速找到哪些點在這個奇環中,顯然$u$和$v$必定都出如今交錯樹上($type$不爲$0$),結合上面的那張圖考慮,奇環的範圍就是兩個點在交錯樹上的鏈中包含的全部點,所以咱們須要找到這兩個點的$lca$,這裏直接採用暴力向上跳的作法便可。
找到之後怎麼鏈接$pre$邊呢?咱們參考一下文章開頭的結構,能夠發現此時的$lca$必定就是那個花中惟一的沒有匹配或者匹配到外面節點的$1$號點。所以感性思考一下,咱們應該「誘導」其餘全部的點經過$pre$和$match$邊一路走走到$lca$上,所以將除了與$lca$相連的$pre$邊外其餘的$pre$邊都改成雙向邊,並將$u$和$v$也改爲雙向邊便可達到這個目的。
最後作一點掃尾工做,咱們能夠經過並查集將奇環縮成一個點(所以在普通增廣的時候也要考慮並查集的狀況),不妨用$lca$作這朵花的表明。同時再考慮一下這個新點的$type$值應該設爲何,由於每一個橙色點最多隻有一個兒子(它的匹配點),所以$lca$必定是藍色點,所以新點的$type$值爲$1$,因此要注意將花中全部$type$值爲$2$的點修改成$1$而且加入隊列。
當咱們找不到新的點時,本次增廣失敗。
===============分割分割================
以上就是通常圖最大匹配的增廣過程,注意在每次增廣以前,$pre$,$type$以及並查集都是要初始化的。將並查集的複雜度看做常數,則每次增廣至可能是$O(m)$的,一共須要增廣$O(n)$次,所以帶花樹算法的複雜度和匈牙利同樣,都是$O(nm)$,固然,在實踐中帶花樹算法跑得通常會比理論上界快不少。
相信在熟練理解帶花樹的過程後必定能寫出代碼,所以爲了避免對讀者形成思惟定式影響這裏就不貼代碼了。完結撒花~