并查集就是给你一个图,里面有很多点,并就是把点合并一个集合,把a点搞为b点父亲;查就是查点在哪个集合,查a点的父亲祖先是谁。
初始化点的父亲是自己
int find(int x){ if(father[x] != x)//如果父亲不是自己,那就递归找父亲 father[x] = find(father[x]);//找他父亲时把其儿子也连上,大多数这样压缩下就行,也可以写find(father[x])但没压缩 return father[x];//找到父亲返回他 } //查找递归栈爆了的话用迭代 int find(int x){ int y = x; while(father[y] != y){ y = father[y];//把原x的爸爸赋值给y,再回检查y的爸爸是否等于y } //压缩,爸爸直接一步到位 int p = x, j; while(p != y){//就是把从x的父亲的父亲的...的父亲(如果有)全部直接一步到祖先, j = father[p];//不用一步步找 father[p] = y; p = j; } return y; } void union(int x, int y){ int p = find(x);//找x父亲 int q = find(y);//找y父亲 if(p != q){ father[p] = q;//如果不是同一个父亲, //那就把q作为p父亲,father[q]=p也可以 } } //启发式合并优化,加个rank数组初始化为0,存他查找的深度,把深度小挂在大的上 void union(int x, int y, int rank[]){ int p = find(x); int q = find(y); if(p != q){ if(rank[p] < rank[q]){ father[p] = q;//这样就不会加深度 } else if(rank[p] > rank[q]){ father[q] = p;//同上 } else{ father[q] = p;//随便搞个父亲,父亲深度加1 rank[p]++; } } }
例:luogu p1525并查集时区分敌友
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 2e5 + 5; ll n, m, k, sum; string s; priority_queue<int, vector<int>, greater<int> >q; struct node{ int x, y, z;//z为怨气值 }a[maxn]; int f[maxn], b[maxn]; void initialise(){//f初始化,各自在自己监狱 for(int i = 0; i < maxn; i++){ f[i] = i; } } int find(int x){ if(f[x] != x){ f[x] = find(f[x]); } return f[x]; } bool cmp(node a, node b){//sort排序 return a.z > b.z; } void union(int x, int y){ int p = find(x); int q = find(y); if(p != q){ f[p] = q; } } int main(){ //ios_base::sync_with_stdio(0); //cin.tie(); cout.tie(); cin>>n>>m; initialise(); for(int i = 1; i <= m; i++) cin>>a[i].x>>a[i].y>>a[i].z; sort(a + 1, a + m + 1, cmp);//按他怨气值大小排序,大的在前 for(int i = 1; i <= m; i++){ int p = find(a[i].x); int q = find(a[i].y); if(p == q){//如果同个监狱,直接输出怨气值,因为排了序 cout<<a[i].z; return 0; } //如果a[i].x没有敌人,把a[i].y设为他敌人互为敌人不能关一起 if(!b[a[i].x]){ b[a[i].x] = a[i].y; } else{//a[i].x有敌人(就是b[a[i].x]),把他敌人和a[i].y关一起,这样关一起的怨气值肯定没和原先高,因为排了序 union(b[a[i].x], a[i].y); } if(!b[a[i].y]){//同上 b[a[i].y] = a[i].x; } else{//同上 union(b[a[i].y], a[i].x); } } cout<<0; return 0; }
例:luogu p2024同上题,不过是3个类
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 3e5 + 5; int n, m, k, sum; string s; queue<int> q; int f[maxn];//一倍存自己,两倍存朋友,三倍存敌人,在这一倍是a类,2倍是b类,3倍是c类 //各自倍的如果是同集合,说明相互是敌人;同倍的在同一集合,说明是自己人 void initialise(){ for(int i = 0; i < maxn; i++){ f[i] = i; } } int find(int x){ if(f[x] != x){ f[x] = find(f[x]); } return f[x]; } void union(int x, int y){ int p = find(x); int q = find(y); if(p != q){ f[p] = q;//这是p吃q的意思,也可以f[q]=p,看指向爸爸是哪种意思 } } int main(){ //ios_base::sync_with_stdio(0); //cin.tie(); cout.tie(); initialise(); cin>>n>>m; int x, y, z; for(int i = 1; i <= m; i++){ cin>>z>>x>>y; if(x > n || y > n){//依题意,假话 sum++;continue; } if(z == 1){//同类的话,找一倍的x和在2倍的y,和一倍x和三倍y是不是同集合,同集合那就不是同类 if(find(x) == find(y + n) || find(x) == find(y + n + n)){ sum++;continue; } //不符合上面的情况说明是真话,那把各自同倍(也就是各自类)的合并一起 union(x, y); union(x + n, y + n); union(x + n + n, y + n + n); } else if(z == 2){//x吃y,那判断下同倍xy在不在同集合,是的话就假话;还判断是不是y吃x,找三倍y和一倍x在不在同集合,在就是y吃x if(find(x) == find(y) || find(x) == find(y + n + n)){ sum++;continue; } //不符合上面的话代表是真话 union(x + n + n, y);//c类x吃a类的y(也就是三倍的x吃一倍的y) union(x, y + n);//a类x吃b类y(也就是一倍的x吃二倍的y) union(x + n, y + n + n);//b类x吃c(也就是二倍的x吃三倍的y) } } cout<<sum; return 0; }
例:luogu p1196并查集时兼顾维护
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 3e4 + 5; ll n, m, k, sum; string s, r; int num[maxn], dis[maxn], f[maxn]; //num是是该数字在组的成员数量,dis是该数离他组组头的距离 int find(int x){ if(f[x] != x){ int r = f[x]; f[x] = find(f[x]); dis[x] += dis[r];//如果x有父亲,就是和他父亲在同列,那x到队头的距离也就变长了此时dis[x]只代表到他父亲距离,要加上他父亲到队头距离 num[x] = num[f[x]];//x所在列的数量也就是他父亲所在列的数量 } return f[x]; } void union(int x, int y){ int p = find(x); int q = find(y); if(p != q){ f[p] = q;//p父亲是q,p所在列移动到q所在列 dis[p] = dis[q] + num[q];//合并要移动列,p到队头距离增加到原q到队头距离加上q所在列的原成员数量 num[q] += num[p];//q所在列数量要加上p所在列数量 num[p] = num[q];//p所在列的数量和他父亲所在列数量一样 } } void query(int x, int y){ int q = find(x); int p = find(y); if(q != p){ cout<<-1<<endl; } else{//输出他俩之间距离,记着要减一 cout<<abs(dis[x] - dis[y]) - 1<<endl; } } int main(){ //ios_base::sync_with_stdio(0); //cin.tie(); cout.tie(); for(int i = 1; i <= maxn; i++) f[i] = i, num[i] = 1; cin>>n; char c; while(n--){ cin>>c>>m>>k; if(c == 'm'){ union(m, k); } else{ query(m, k); } } return 0; }
例:luogu p2256用map字符串映射
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 2e5 + 5; ll n, m, k, sum; string s, r; priority_queue<int, vector<int>, greater<int> >q; map<string, string>ma; string f[maxn], a[maxn]; void initialise(){ for(int i = 0; i < maxn; i++){ ma[a[i]] = a[i]; } } string find(string x){ if(ma[x] != x){ ma[x] = find(ma[x]); } return ma[x]; } void union(string x, string y){ string p = find(x); string q = find(y); if(p != q){ ma[p] = q; } } int main(){ //ios_base::sync_with_stdio(0); //cin.tie(); cout.tie(); cin>>n>>m; for(int i = 1; i <= n; i++){ cin>>a[i]; } initialise(); for(int i = 1; i <= m; i++){ cin>>s>>r; union(s, r); } cin>>k; while(k--){ cin>>s>>r; string p = find(s); string q = find(r); if(p == q) cout<<"yes."<<endl; else cout<<"no."<<endl; } return 0; }
0 条评论