并查集以及应用

参考 http://blog.csdn.net/dm_vincent/article/details/7655764


动态连通图可能的操作
查询节点属于的组:数组对应位置的值即为组号
判断两个节点是否属于同一组:分别得到两个节点的组号,然后判断组号是否相等
连接两个节点:分别得到两个节点的组号,组号相同时,退出操作,组号不同时,通过判断组大小进程连接
获取组的数目:初始化为整个数组的大小,每次连接后,递减一

class UF;
Union(int i,int j); 连接
int Find(int i); 查找组号
BOOL Cnnected(int i,int j) 判断是否为同一组
int Count();  返回组号


class UF
{
private :
int* id;
int count;
public:
int* sz;
UF(int N)
{
count = N;
id = new int[N];
sz = new int[N];
for (int i = 0; i < N; i++)
{
id[i] = i;
sz[i] = 1;
}
}


int Find(int i)  //路径压缩
{
while (i != id[i])
{
id[i] = id[id[i]];  //将父节点设置为爷爷节点
i = id[i];
}
return i;
}
int Count()
{
return count;
}
BOOL Connected(int i, int j)
{
return Find(i) == Find(j);
}
VOID Union(int i, int j)
{
int a;
int b;
a = Find(i);
b = Find(j);
if (Find(i) == Find(j))
{
return;
}
if (sz[i] < sz[j])   //比重
{
id[a] = b;
}
else
{
id[b] = a;
}
count--;
}
};

int main()
{
UF uf(10);
int i;
int j;
int V1[6][2] = { 1,2,4,5,7,6,3,9,7,2,3,2 };
for (i = 0; i < 6; i++)
{
uf.Union(V1[i][0], V1[i][1]);
}
j = uf.Count();
printf("组号 %d", j);


    return 0;
}


-----------------------------------------------------------------------------------------------
 
poj上一个题目  个人解题的思路 也有借鉴  

A吃B B吃C C吃A 总共树林里有三种动物,N个动物   现在有一定数量的话 要求判断真假 并输出假话个数  
解题思路:
不同于普通的并查集,这个题目节点直接存在一定的利害关系,故考虑这是一个带权并查集,即确定每个节点所带的数值来表示其与父节点或子节点的关系。

一,确定权值(这里应该是相对于子节点)
现在定义如下
取 0 1 2 来表示关系,原因:第一 父节点和子节点关系有三种——同类 相对于子节点(被父节点吃 和吃父节点) 相对于父节点(吃子节点和被子节点吃)  第二 题意所需  在后续会明白

0  表示父节点和子节点同类
1  表示子节点被父节点吃或父节点吃子节点
2  表示子节点吃父节点或父节点被子节点吃

二,初始化
对每个节点进行初始化 
节点结构  
struct animal{
int relation;
int parents;}
在初始化时每个节点按序赋值 ,relation为1(自己与自己是同类),parents=id(自己)

压缩路径
在每一次查询的过程中,将子节点连接到爷爷节点上  
animal[i].parents = animal[animal[i].parents].parents;

这里就出现了这个问题的核心 在压缩路径后,由于破坏了原有的状态,如何根据原有的信息,来得到新的信息。
即如何确定子节点和爷爷节点的relation
这里采用枚举法观察
子节点        父节点      子节点和爷爷节点的关系
  0              0                0
  0              1                1
  0              2                2
  1              0                1
  1              1                2
  1              2                3
  2              0                2
  2              1                0
  2              2                1

不难发现 这里子节点和爷爷节点的关系可写为
r[子爷] = (r[子] + r[父])%3

现在处理稍复杂情况连接  
x->y->p     x->q  现在将两个进行合并  即将两个根节点相连

这里处理方法为
1. 通过x->q推出 q相对于x的关系  
这里通过枚举
子节点        父相对于子
  0              0
  1              2
  2              1
规律为 r[父相对于子] =   (3- r[子])%3

现在过程为 
2.x->y->p  在通过x找根节点时 将x连接到p上
这里 r[x1] = (r[x]+r[y])%3
对于x->q  
r[q相对于x] = (3-r[x])%3
现在等价为q->x->p  将q连接到p上 
r[q] =(r[x1] + r[q])
= (r[x] + r[y] + (3-r[x])%3 )%3


Union函数 
最为复杂 这里情况多变但由于存在路径压缩 所以知树的高度一般最多不会超过三层。故最长只会出现
x1->x2->x3  与y1->y2->y3 进行连接 这应该是最复杂的情况  
首先在处理时确定一个前提 那就是 只有从y到x的连接  就是每句话的后一个向前一个进行连接

现在是最简单的 
situaion 1:
1  x1 y1 (x1 and y1 are all the roots at this situation)
solution:
Find(x1)
change relaion(x1)
Find(y1)
change relaion(y1)   //nothing happen

y1.parent = x1
y1.relation = 1-1 


situation 2:
2  x1  y1 (x1 and y1 are all the roots at this situation)
solution:
Find(x1)
change relaion(x1)
Find(y1)
change relaion(y1)   //nothing happen
y1.parent = x1
y1.relation = 2-1


situation 3:
1 x2 y1(x2->x1  y1 is the root)
solution:
Find(x2)   //xroot = x1
change relaion(x2)  
Find(y1)
change relaion(y1)   

y1.parent = x2
y1.relation = 1-1

situation 4:
2 x2 y1((x2->x1  y1 is the root)
solution:
Find(x2)   //xroot = x1
change relaion(x2)  
Find(y1)
change relaion(y1)   

yroot.parent = x2
yroot.relation = 2-1

situation 5:
1 x2 y2((x2->x1  y2->y1 )
solution:
Find(x2)   //xroot = x1
change relaion(x2)  
Find(y2) //yroot = y1
change relaion(y2)   

yroot.parent = xroot
yroot.relation = ((3-r[yroot])%3 + d - 1(话中的数) + r[x2])%3 

而对于更加复杂的情况 可用上述式子进行等效替换 故不进行赘述。

这里是大致思想
代码就不进行粘贴

你可能感兴趣的:(并查集以及应用)