pku2492 并查集

题目链接:A Bug's Life

分析:很经典的一道并查集,这题关键在如何维护每一个点到集合顶点的偏移量。第一次了解偏移量,还是挺有收获的!对于两个点x,y,分别找到他们的根节点fx,fy。(fx = find(x); fy = find(y);)如果fx == fy 则找到一对同性恋者,return,否者:bin[fx] = fy; fx指向fy,为了保证x和y为异性,即相对根节点的偏移量不同,fx的偏移量offset[fx] = (offset[x] + offset[y] + 1); 是同或关系,这样只对根节点操作,就不影响其它各点间的相互关系。还有:在路径压缩的过程中,采用递归形式,由根节点反向改变每个叶子节点的值,这样只用比较当前节点和他父节点之间的关系了:offset[r] = (offset[r] + offset[bin[r]]) % 2;是异或关系。

 

代码
   
     
#include < stdio.h >
#include
< stdlib.h >
#define NN 2005
int bin[NN];
int offset[NN];
int ok;
int find( int x){
int r = x;
if (bin[r] == r){
return r;
}

int t = find(bin[r]);

offset[r]
= (offset[r] + offset[bin[r]]) % 2 ;

bin[r]
= t;

return bin[r];
}
void merge( int x, int y){
int fx, fy;
fx
= find(x);
fy
= find(y);

if (fx == fy){
if (offset[x] == offset[y]){
ok
= 1 ;
return ;
}
}
else {
bin[fx]
= fy;
offset[fx]
= (offset[x] + offset[y] + 1 ) % 2 ;
}
}
int main()
{
int n, sce, i, nbug, nint, a, b;
scanf(
" %d " , & n);
for (sce = 1 ; sce <= n; sce ++ ){
scanf(
" %d%d " , & nbug, & nint);
for (i = 1 ; i <= nbug; i ++ ){
bin[i]
= i;
offset[i]
= 0 ;
}
ok
= 0 ;
while (nint -- ){
scanf(
" %d%d " , & a, & b);
merge(a, b);
}

printf(
" Scenario #%d:\n " , sce);
if (ok) puts( " Suspicious bugs found! " );
else puts( " No suspicious bugs found! " );
puts(
"" );
}
return 0 ;
}

这种用递归形式压缩路径的方法还是第一次写,学习了!正是因为是递归形式,才能实现由根节点层层反向改变的要求。其中offset记录的是每个点和它的父节点的偏移量。如果父节点是根节点,那当然就是和根节点的偏移量了,这也就是遍历到根节点的路径找到和根节点的偏移量。

附大牛详解

你可能感兴趣的:(并查集)