帶花樹演算法學習小記

2022-09-14 19:03:11 字數 2363 閱讀 7319

眾所周知,帶花樹演算法用來解決一般圖最大匹配問題。

因為人很菜理解不清楚,所以建議對板理解。

以下都是本人的感性理解,有不嚴謹的說法請包涵。

同樣是找增廣路。增廣路定義為一條路徑\(p_1,p_2,\dots,p_k\),滿足\(p_2,p_3\)匹配,\(p_4,p_5\)匹配……\(p_,p_\)匹配。\(p_1\)和\(p_k\)不在匹配中、

如果找到了增廣路,把增廣路上點調整一下匹配關係,就可以使匹配數增多。

設\(bel_x\)表示\(x\)和哪個點匹配。還有\(pre_x\)表示\(bfs\)樹上的前驅。

找增廣路大概流程:bfs,遍歷過的點要記錄顏色。\(s\)不在匹配中,\(s\)為起點,一開始\(s\)為黑色。

設當前點為\(u\),列舉其一條邊\(v\)。

如果\(v\)沒有被遍歷過:(1)\(v\)不在匹配中,此時找到增廣路。(2)\(v\)在匹配中,把\(v\)標記為白點,\(bel_v\)標記為黑點,把\(bel_v\)丟入佇列。

如果\(v\)被遍歷過:

如果\(v\)為白點,不管它。

如果\(v\)為黑點,進行開花操作。

演算法核心:開花。

如果開花一定是找到了個奇環。定義此時\(u,v\)在bfs樹上的lca為花根,記為\(rt\)。

考慮從花上的某個點出去,並且找到了增廣路。那麼一定可以從\(rt\)開始(其中\(rt\)是黑點),可以通過走花的一邊,使得走的這一邊都是白點-黑點交替。(這種說法並不準確,看著辦吧)這條路徑是頭是黑點,尾是黑點,所以可以看成把花縮到花根上。後面找到增廣路的時候就把花展開,展開的時候選一條花根到花中連出去的點的合法的路徑。

實現是問題。

每個需要記錄前驅。然而如果在花中可能前後兩個點都是前驅,都要記下來。

為了方便實現,\(pre_x\)只是記\(x\)為白點意義下的前驅(因為在花中,\(x\)可能作為黑點,可能作為白點)。如果\(x\)為黑點,那麼它往前兩步是:\(x\to bel_x \to pre_\)。這樣就不需要另外記另乙個前驅了。

求lca時,輪流跳,跳到了之後打標記,直到跳到有標記的點為止。遍歷次數是\(2\max(dis(u,rt),dis(v,rt))=o(花大小)\)(之前縮的花算乙個點)。

開花:給花上的原來黑點的位置,計算它為白點意義下的前驅\(pre_x\),此時\(pre_x\)將指向不同於\(bel_x\)的方向。並且把這些白點都標記為黑點,丟入佇列(此時標黑表示是否有丟入佇列。)。(然而在實現的時候,開花操作是對沒有縮點的圖進行操作。)

用並查集來維護出每個點當前在哪個花中。

更多細節見**注釋。

怎麼網上的好多板子說總時間是\(o(nm*並查集時間)\),為什麼我感覺下面這個照著別人板子寫了板子的時間複雜度是\(o(n^3)\)?

一次縮點會少至少兩個點,所以會進行\(o(n)\)次開花操作。然而這個板子裡開花時間是\(o(n)\)的。於是一次增廣時間應該是\(o(n^2)\)。

理論上確實也可以做\(o(m)\)吧,就是保證如果乙個點被縮掉,開花的時候就不會遍歷它。但不知道有什麼簡潔的實現方法。

如果有神仙路過希望指點。

using namespace std;

#include #define n 1005

#define m 200005

int n,m;

struct edge e[m*2];

int ne;

edge *last[n];

void link(int u,int v);

last[u]=e+ne++;

}queueq;

int c[n];

int bel[n],pre[n],fa[n];

int getfa(int x)

int lca(int x,int y)

return x;

}void blossom(int x,int y,int w)

}int find(int s)

q.push(s);

c[s]=1;

while (!q.empty())

c[y]=0,c[bel[y]]=1;

q.push(bel[y]);

}else if (c[y]==1)

} }return 0;

}int main()

int ans=0;

for (int i=1;i<=n;++i)

if (!bel[i])

ans+=find(i);

printf("%d\n",ans);

for (int i=1;i<=n;++i)

printf("%d ",bel[i]);

return 0;

}

帶花樹演算法學習筆記

難得yyb寫了乙個這麼正式的標題 q 為啥要學帶花樹這種東西啊?a 因為我太菜了,要多學點東西才能不被吊打 q 為啥要學帶花樹這種東西啊?a 因為我做自己的專題做不動了,只能先去 預習 ppl的專題了 q 為啥要學帶花樹這種東西啊?a 因為可以用來做題啊,比如某wc題目 先推薦乙個很皮很皮的帶花樹講...

帶花樹演算法學習筆記

帶花樹演算法大概就是解決一般圖的最大匹配 回顧匈牙利演算法解決二分圖匹配 我們每次增廣左側的a,其實是找到與它有邊相連的右側的乙個點b 看b是否在匹配中,如果不在那麼增廣成功 如果在那麼就增廣b的匹配點c看是否成功 但我們發現這是因為我們把點集分成兩個內部無交的點集才可以這麼做 但是對於一般圖是不滿...

帶花樹學習筆記

帶花樹是一種用於解決一般圖最大匹配的演算法 不是資料結構 一般圖與二分圖最大的不同是一般圖中存在奇環,所以不能和二分圖一樣的隨便找到一條交錯路就增廣。帶花樹的做法是把奇環縮起來 開花 讓環上的點找到乙個增廣路之後能沿著正確的方向往回退,除此之外在思路上與匈牙利差不多。具體看 的注釋 include ...