【並查集】並查集
模板
數組版:
int parent[MAX_N]; int rank[MAX_N]; void Init(int n){ for(int i = 0; i < n; ++i){ parent[i] = i; rank[i] = 0; } } int Find(int x){ if(parent[x] = x){ return x; } else { return Find(parent[x]); } } void Union(int x, int y){ x = Find(x); y = Find(y); if(x == y) return; if(rank[x] < rank[y]){ parent[x] = y; } else { parent[y] = x; if(rank[x] == rank[y]) rank[x]++; } }
結構體版:
struct Node { int rank; int parent; } node[MAX_N]; void Init(int n){ for(int i = 0; i < n; ++i){ node[i].parent = i; node[i].rank = 0; } } int Find(int x){ if(node[x].parent = x){ return x; } else { return Find(node[x].parent); } } void Union(int x, int y){ x = Find(x); y = Find(y); if(x == y) return; if(node[x].rank < node[y].rank){ node[x].parent = y; } else { node[y].parent = x; if(node[x].rank == node[y].rank) node[x].rank++; } }
例題
例1 poj 1182(帶權並查集)
解題思路:
這個題是非常經典的並查集問題。並查集作用:查詢a和b是否屬於同一組;合並a和b所在的組。註意並查集無法進行分割操作。利用題目中動物之間的相對關系可以建立一個並查集。對每一個元素,把它對父節點的關系用數組rank[i]表示,即relation,作為權值。0:與父節點同類 1:吃父節點 2:被父節點吃。
路徑壓縮。為了使查找效率提高,我們需要使樹的高度盡可能小,讓它只有一層,即在查找的過程中,把樹中一條路徑上的所有點都連在根節點上,從而加快了查找速度,與此同時,還要更新與此節點有關的其他變量,比如此節點與父節點的權值(父節點變為根節點),該樹的高度等等。
rank[x] rank[y] x與z的關系權值 0 0 0=(i + j)%3 0 1 1=(i + j)%3 0 2 2=(i + j)%3 1 0 1=(i + j)%3 1 1 2=(i + j)%3 1 2 0=(i + j)%3 2 0 2=(i + j)%3 2 1 0=(i + j)%3 2 2 1=(i + j)%3
推導公式:rank[x] = (rank[x] + rank[y]) % 3; 即對於i節點,更新公式為:rank[i] = (rank[i] + rank[parent[i]]) % 3。不過還有更簡便的方法:模擬向量的運算x->z = x->y + y->z,所以rank[x] = (rank[x] + rank[y])% 3。對3取模是為了保證結果為0,1,2。
最後是集合的合並操作。合並操作並不復雜,復雜的是更新集合間的關系(即集合根節點的關系)。這裏有大神使用向量的形式來計算更新關系權值,假設需要合並x和y,查詢得知x和y的根節點分別為:x_p,y_p,如果將x_p連接到y_p,那麽rank[x_p]即為x_p與根節點y_p的關系。x_p->y_p = x_p->x + x->y + y->y_p = (-x->x_p + x->y + y->y_p)。所以更新公式為rank[x_p] = (-rank[x] + type - 1 + rank[y] + 3) % 3。(加上3是為了防止出現負數的情況;對3取模是為了保證結果的取值範圍)。type即為輸入的num。type為1,x與y同類,type為2,x吃y。
Solution:from 專註如一
#include <cstdio.h> #include <cstring> #include <algorithm> using namespace std; const int MAX_N = 50000 + 10; int parent[MAX_N], rank[MAX_N]; void Init(int n){ for(int i = 0; i < n; ++i){ parent[i] = i; rank[i] = 0; } } int Find(int x){ if(parent[x] == x){ return x; } int y = Find(parent[x]); rank[x] = (ran[x] + ran[parent[x]]) % 3; return parent[x] = y; } int Union(int x, int y, int type){ x_p = Find(x); y_p = Find(y); if(x_p == y_p){ if((rank[x] - rank[y] + 3) % 3 == type - 1) return 0; return 1; } parent[x_p] = y_p; rank[x_p] = (-rank[x] + type - 1 + rank[y] + 3) % 3; return 0; } int main(){ int n, k, res = 0; int type, x, y; scanf("%d %d", &n, &k); Init(n); for(int i = 0; i < k; ++i){ scanf("%d %d %d", &type, &x, &y); if(x == y && type == 2) ++res; else if(x > n || y > n) ++res; else res += Union(type, x, y); } printf("%d\n", res); return 0; }
【並查集】並查集