學習帶權並查集之前我們需要先對並查集和路徑壓縮壓縮了解,有需求的可以參考這篇博客。
剛昨天總結了並查集的相關操作,今天做題的時候居然發現自己一直都是存在一些想不到的地方,總是會存在一些漏洞,最騷的是今天做到了食物鏈這道題......才知道了帶權並查集和種類並查集......好了接下
來就要進入帶權並查集了。
上面的博客中有寫到路徑壓縮的相關操作,我們知道路徑壓縮的結果就是同一集合中的所有元素統一指向樹根,這樣可以加快訪問速度。和路徑壓縮一樣,帶權並查集僅僅是在每個集合元素都指向樹根
的前提下賦予這些結點相對於樹根的權值。
那么我們就需要用一個數組一個元素相對於他所屬集合樹根的權值,我們用value存儲。
我們已經知道了基本並查集的一般實現,但是如果加入了value要如何實現呢。
我們思考:
在find函數中,我們對於每個元素進行一次賦值,使得最終所有元素的父親結點都指向樹根結點,那么如果帶有權值的話,我們知道每次都會改變一個結點的父親,知道找到他所屬集合的樹根,所以
我們每次也需要更新對應的value值。
那么合並函數呢,我們知道每次合並我們會將x所屬集合的樹根px的父親設置為y所屬集合的樹根py,這樣就可以將兩個集合合並,那么我們知道在這過程中我們改變了px的父親,也就需要改變px對
應他父親的權值,我們根據下圖就可以知道,對於value[px],因為x最終父親為py,所以顯然兩條路的權值應該相等,我們就可以得出如下代碼。
並不是所有類型的更新都是下面這種情況,具體情況具體分析,但是思路寶貴。
1 #include <cstdio> 2 #include <cstring> 3 using namespace std; 4 5 const int maxn = 10000 + 5; 6 int head[maxn], value[maxn], rank[maxn]; 7 8 int find(int u) { 9 if(u != head[u]) { 10 int t = head[u]; 11 head[u] = find(head[u]); 12 value[u] += value[t]; 13 } 14 return head[u]; 15 } 16 17 void Union_Set(int x, int y, int s) { 18 int fx = find(x), fy = find(y); 19 if(fx == fy) return; 20 if(rank[fx] > rank[fy]) { 21 head[fy] = fx; 22 value[fy] = -value[y] + value[x] + s; 23 } else { 24 head[fx] = fy; 25 value[fx] = -value[x] + value[y] + s; 26 if(rank[fx] == rank[fy]) rank[fy] ++; 27 } 28 } 29 30 int is_same(int x, int y) { 31 return find(x) == find(y); 32 } 33 34 int main () { 35 36 return 0; 37 }
后續會更新相關題目:針對題目再做詳細描述。