并查集 : 以不相交集合为基础的抽象数据类型
· 支持运算:
1.UFunion(A,B,U):将并查集U中的集合A和B合并,其结果取名为A或B
2.UFfind(e):找出包含元素e的集合,并返回该集合的名字
· 实现思想:
每个集合用一棵树表示,树的结点用于存储集合中的元素名和一个指向其父节点的指针,树根结点的元素代表该树所表示的集合。
· 用父节点数组实现
数组 parent[x] 表示元素的父节点。
但在最坏情况下,n个结点的树可能退化为一条链。此时对所有元素各执行一次UFfind耗时O(n^2)。
改进方法:①按结点个数合并(结点少的合并到多的上,用parent[root] = -n记录根为root的树的结点个数n);②按高度合并(高度低的合并到高的上,记录方式同上);③按压缩路径(O(n(α(n))),α(n) < 4)
下面是泡泡的并查集模板
struct UF
{
int fa[MAX_N];
void init() { for(int i = 1; i < MAX_N; i++) fa[i] = i;}
int find(int a) { return fa[a] == a ? a : fa[a] = find(fa[a]);}
void mix(int a,int b) { fa[find(a)] = find(b);}
bool isSame(int a, int b) { return find(a) == find(b);}
} uf;
//–作业题
12.4 山海经
(题目很复杂其实只是想问两节点间的差值,有些已经能判断有些还不能,也即集合间的关系为是否已经能判断差值,能互相判断差值的扔进同一集合。如果AB间差值还不能判断,则语句d A B则把AB集合合并下并且AB结点之间差值为d。如果已经能判断则运算下是否为d以判断真假。差值的算法为,记录下集合中每个结点到根节点的差值qua,那么同一集合中的两节点ab就能通过a-fa-b的路径得到,在合并集合的时候更新a集合根节点的fa为b并且fa的qua通过fa-a-b-fb更新,合并的时候只要更新根节点就好,在find中回溯一边更新其他节点的qua。)
#include<iostream>
#include<cstdio>
using namespace std;
#define MAX 50000+7
int pre[MAX];
int qua[MAX];//与pre的犇势差
int m,n,k,i;
int find(int x)
{
int t;
if(x != pre[x])
{
t = pre[x];
pre[x] = find(pre[x]);
qua[x] = (qua[x] + qua[t])%m; //回溯更新quax
}
return pre[x];
}
void mix(int a, int b,int d)
{
int fa = find(a);
int fb = find(b);
if(fa != fb)
pre[fa] = fb;
qua[fa]=(m-qua[a]+d+qua[b])%m; //fa-a-b-fb 更新quafa
}
int main()
{
scanf("%d%d%d",&m,&n,&k);
for(i = 1; i <= n; i++)
{
pre[i] = i;
qua[i] = 0;
}
int d,a,b;
int ans = 0;
for(i = 1; i <= k; i++)
{
scanf("%d%d%d",&d,&a,&b);
if(a > n || b > n || d >= m )
{
ans++;
continue;
}
int fa = find(a);
int fb = find(b);
if(fa == fb)
{
if((qua[a]+m-qua[b])%m != d) //a-f-b ab犇势差不为d
ans++;
}
else mix(a,b,d);
}
printf("%d\n",ans);
return 0;
}
12.2 直通车
(朋友的关系可传递,敌人的关系不可传递。所以用并查集记录朋友关系,用二维数组记录敌人关系即可。)
#include<iostream>
#include<cstdio>
using namespace std;
#define MAX 100+7
int pre[MAX];
int mp[MAX][MAX];
int n,m,k,i,j;
void init()
{
int i;
for(i = 1; i <= n; i++)
pre[i] = i;
}
int find(int x)
{
int r = x;
while(r != pre[r])
r = pre[r];
int i = x,j;
while(i != pre[i])
{
j = pre[i];
pre[i] = r;
i = j;
}
return r;
}
void mix(int a, int b)
{
int fa = find(a);
int fb = find(b);
if(fa != fb)
pre[fa] = fb;
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
init();
int x,y,op;
for(i = 1; i <= m; i++)
{
scanf("%d%d%d",&x,&y,&op);
mp[x][y] = op;
mp[y][x] = op;
if(op == 1) mix(x,y);
}
for(i = 1; i <= k; i++)
{
scanf("%d%d",&x,&y);
int fx = find(x);
int fy = find(y);
if(fx == fy && mp[x][y] != -1) printf("Good job\n");
else if(fx != fy && mp[x][y] != -1) printf("No problem\n");
else if(mp[x][y] == -1)
{
if(fx == fy) printf("OK but...\n");
else printf("No way\n");
}
}
return 0;
}