并查集小结 (参考birdfly+修改)

    并查集的作用:并和查,即合并和查找,将一些集合合并,快速查找或判断某两个集合的关系,或某元素与集合的关系,或某两个元素的关系。

    并查集的结构:并查集主要操作对象是森林,树的结构赋予它独特的能力,对整个集合操作转换为对根节点(或称该集合的代表元素)的操作,一个集合里的元素关系不一定确定,但相对于根节点的关系很明了,这也是为了查找方便。

    并查集优化方法:按秩合并和路径压缩的配合使用,使得查找过程优化到极致。按秩合并,每次将深度小的树合并到深度大的树里面去,使得整棵树尽量矮;路径压缩,将当前节点到根节点路径上的所有点直接连到根节点上(有递归和非递归两种方法),使得每个点到根节点的距离更短,在下一次查找的时候更快。

 

如何快速确定偏移量公式:

例:现在要合并节点x,y, 找到根节点fx = Find(x); fy = Find(y);一般情况下,根节点的偏移量都保持为0, offset[foot] = 0;如果要使得x和y的偏移量为t,假设fx指向fy,则可以写出公式offset[x] + offset[fx] - offset[y] = t,则offset[fx] = (offset[y] + t - offset[x]) % n; 这个n即为总共有多少类,如:在poj1182 食物链中n = 3,,在poj2492 A Bug's Life中n = 2, 这样fx的偏移量就计算出来了,只需要改其中一个根节点的偏移量,这里是fx,因为假设是fx指向fy。

Birdfly并查集小结

孟孟起并查集转帖

Slyar(并查集讲解)

并查集学习小节(POJ版))

对应题目和代码

PKU1611The Suspects

ContractedBlock.gifExpandedBlockStart.gif代码
 
   
#include<stdio.h>
#define N 3005
int bin[N],cnt[N];
int find(int
x)
{
int r=
x;
while(bin[r]!=
r)
r
=
bin[r];
int y=
x;
while(bin[y]!=
y){
y
=
bin[y];
bin[y]
=
r;
}
return
r;
}

void merge(int x,int
y)
{
int fx=
find(x);
int fy=
find(y);
if(fx!=
fy){
if(fx==0
){
bin[fy]
=
fx;
cnt[fx]
+=
cnt[fy];
}
else if(fy==0
){
bin[fx]
=
fy;
cnt[fy]
+=
cnt[fx];
}
else
{
bin[fx]
=
fy;
cnt[fy]
+=
cnt[fx];
}
}
}

int
main()
{
int
n,m,num,cur,next,i;
while(scanf("%d%d",&n,&m),n+
m){
for(i=0;i<n;i++
)
bin[i]
=i, cnt[i]=1
;
while(m--
){
scanf(
"%d%d",&num,&
cur);
while(--
num){
scanf(
"%d",&
next);
merge(cur,next);
cur
=
next;
}
}
printf(
"%d\n",cnt[0
]);
}
return 0
;
}

PKU2492 A Bug's Life

分析:很经典的一道并查集,这题关键在如何维护每一个点到集合顶点的偏移量

。第一次了解偏移量,还是挺有收获的!

对于两个点xy,分别找到他们的根节点fxfy。(fx = find(x); fy = find(y);

如果fx == fy 则找到一对同性恋者,return

否者:bin[fx] = fy; fx指向fy,为了保证xy为异性,即相对根节点的偏移量不同,

fx的偏移量offset[fx] = (offset[x] + offset[y] + 1)%2; 是同或关系,

这样只对根节点操作,就不影响其它各点间的相互关系。

还有:在路径压缩的过程中,采用递归形式,由根节点反向改变每个叶子节点的值,

这样只用比较当前节点和他父节点之间的关系了:offset[r] = (offset[r] + offset[bin[r]]) % 2;是异或关系。

 

ContractedBlock.gif ExpandedBlockStart.gif 代码
 
   
#include < stdio.h >
#include
< string .h >
int bin[ 2002 ],offset[ 2002 ],ok;
int find( int x)
{
int r = x;
if (r == bin[r])
return r;
int t = find(bin[r]);
offset[r]
= (offset[r] + offset[bin[r]]) % 2 ;
bin[r]
= t;
return t;
}
void merge( int x, int y)
{
int fx = find(x);
int fy = find(y);
if (fx == fy && offset[x] == offset[y])
ok
= 1 ;
else
{
bin[fx]
= fy;
offset[fx]
= (offset[x] + offset[y] + 1 ) % 2 ;
}
}

int main()
{
int i,cas,ca,n,m,x,y;
scanf(
" %d " , & cas);
for (ca = 1 ;ca <= cas;ca ++ )
{
scanf(
" %d%d " , & n, & m);
for (i = 0 ;i < n;i ++ )
bin[i]
= i, offset[i] = 0 ;
ok
= 0 ;
while (m -- )
{
scanf(
" %d%d " , & x, & y);
if (ok == 1 )
continue ;
merge(x,y);
}
printf(
" Scenario #%d:\n " ,ca);
if (ok == 1 )
puts(
" Suspicious bugs found! " );
else
puts(
" No suspicious bugs found! " );
puts(
"" );
}
return 0 ;
}

 

HDU1232 畅通工程

 

ContractedBlock.gif ExpandedBlockStart.gif 代码
 
   
#include < stdio.h >
int a[ 1002 ];
int find( int x)
{
int r = x;
while (a[r] != r)
r
= a[r];
return r;
}
void merge( int x, int y)
{
int fx = find(x);
int fy = find(y);
if (fx != fy)
a[fx]
= fy;
}
int main()
{
int n,m,i,x,y,ans;
while (scanf( " %d " , & n),n)
{
for (i = 1 ;i <= n;i ++ )
a[i]
= i;
scanf(
" %d " , & m);
while (m -- )
{
scanf(
" %d%d " , & x, & y);
merge(x,y);
}
for (ans =- 1 ,i = 1 ;i <= n;i ++ )
{
if (a[i] == i)
ans
++ ;
}
printf(
" %d\n " ,ans);
}
return 0 ;
}

 

HDU1198 Farm Irrigation

广搜的方法  15MS

ContractedBlock.gif ExpandedBlockStart.gif 代码
 
   
#include < queue >
#include
< iostream >
using namespace std;
char map[ 200 ][ 200 ];
char str[ 52 ][ 52 ];
typedef
struct {
int x, y;
}POINT;
POINT p, next;
int n,m,dir[ 8 ][ 2 ] = {{ - 1 , - 1 }, { - 1 , 0 }, { - 1 , 1 }, { 0 , - 1 }, { 0 , 1 }, { 1 , - 1 }, { 1 , 0 }, { 1 , 1 }};
queue
< POINT > q;
void bfs( int i, int j)
{
int k;
p.x
= i; p.y = j;
q.push(p);
while ( ! q.empty())
{
p
= q.front();
q.pop();
for (k = 0 ; k < 8 ; k ++ ) {
next.x
= p.x + dir[k][ 0 ];
next.y
= p.y + dir[k][ 1 ];
if (next.x >= 0 && next.x <= 3 * m && next.y >= 0 && next.y <= 3 * n)
if (map[next.x][next.y] == ' W ' ) {
map[next.x][next.y]
= ' . ' ;
q.push(next);
}
}
}
}

int main()
{
int i,j,con;
while (scanf( " %d%d " , & m, & n),m >= 0 && n >= 0 )
{
getchar();
for (i = 0 ;i <= 3 * m;i ++ )
{
for (j = 0 ;j <= 3 * n;j ++ )
map[i][j]
= ' . ' ;
}
for (i = 0 ;i < m;i ++ )
{
gets(str[i]);
for (j = 0 ;j < n;j ++ )
{
if (str[i][j] == ' A ' ){map[ 3 * i][ 3 * j + 1 ] = ' W ' ;map[ 3 * i + 1 ][ 3 * j + 1 ] = ' W ' ;map[ 3 * i + 1 ][ 3 * j] = ' W ' ;}
else if (str[i][j] == ' B ' ){map[ 3 * i][ 3 * j + 1 ] = ' W ' ;map[ 3 * i + 1 ][ 3 * j + 1 ] = ' W ' ;map[ 3 * i + 1 ][ 3 * j + 2 ] = ' W ' ;}
else if (str[i][j] == ' C ' ){map[ 3 * i + 1 ][ 3 * j] = ' W ' ;map[ 3 * i + 1 ][ 3 * j + 1 ] = ' W ' ;map[ 3 * i + 2 ][ 3 * j + 1 ] = ' W ' ;}
else if (str[i][j] == ' D ' ){map[ 3 * i + 1 ][ 3 * j + 1 ] = ' W ' ;map[ 3 * i + 1 ][ 3 * j + 2 ] = ' W ' ;map[ 3 * i + 2 ][ 3 * j + 1 ] = ' W ' ;}
else if (str[i][j] == ' E ' ){map[ 3 * i][ 3 * j + 1 ] = ' W ' ;map[ 3 * i + 1 ][ 3 * j + 1 ] = ' W ' ;map[ 3 * i + 2 ][ 3 * j + 1 ] = ' W ' ;}
else if (str[i][j] == ' F ' ){map[ 3 * i + 1 ][ 3 * j] = ' W ' ;map[ 3 * i + 1 ][ 3 * j + 1 ] = ' W ' ;map[ 3 * i + 1 ][ 3 * j + 2 ] = ' W ' ;}
else if (str[i][j] == ' G ' ){map[ 3 * i][ 3 * j + 1 ] = ' W ' ;map[ 3 * i + 1 ][ 3 * j] = ' W ' ;map[ 3 * i + 1 ][ 3 * j + 1 ] = ' W ' ;map[ 3 * i + 1 ][ 3 * j + 2 ] = ' W ' ;}
else if (str[i][j] == ' H ' ){map[ 3 * i][ 3 * j + 1 ] = ' W ' ;map[ 3 * i + 1 ][ 3 * j] = ' W ' ;map[ 3 * i + 1 ][ 3 * j + 1 ] = ' W ' ;map[ 3 * i + 2 ][ 3 * j + 1 ] = ' W ' ;}
else if (str[i][j] == ' I ' ){map[ 3 * i + 1 ][ 3 * j + 2 ] = ' W ' ;map[ 3 * i + 1 ][ 3 * j] = ' W ' ;map[ 3 * i + 1 ][ 3 * j + 1 ] = ' W ' ;map[ 3 * i + 2 ][ 3 * j + 1 ] = ' W ' ;}
else if (str[i][j] == ' J ' ){map[ 3 * i + 1 ][ 3 * j + 2 ] = ' W ' ;map[ 3 * i][ 3 * j + 1 ] = ' W ' ;map[ 3 * i + 1 ][ 3 * j + 1 ] = ' W ' ;map[ 3 * i + 2 ][ 3 * j + 1 ] = ' W ' ;}
else if (str[i][j] == ' K ' ){map[ 3 * i + 1 ][ 3 * j + 2 ] = ' W ' ;map[ 3 * i][ 3 * j + 1 ] = ' W ' ;map[ 3 * i + 1 ][ 3 * j + 1 ] = ' W ' ;map[ 3 * i + 2 ][ 3 * j + 1 ] = ' W ' ;map[ 3 * i + 1 ][ 3 * j] = ' W ' ;}
}
}
con
= 0 ;
for (i = 0 ;i <= 3 * m;i ++ )
{
for (j = 0 ;j <= 3 * n;j ++ )
if (map[i][j] == ' W ' )
{
bfs(i, j);
con
++ ;
}
}
printf(
" %d\n " , con);
}
return 0 ;
}

并查集的方法  0MS

这里用的递归法压缩路径,感觉也挺好的,代码很短,以前都是用非递归形式。在合并的时候,按秩合并很重要,每次合并,总把深度小的树合并到深度大的树里面,以防止树退化成链。这样可以减少树的深度,使得查找时间很均匀,更高效的查找。

ContractedBlock.gif ExpandedBlockStart.gif 代码
 
   
#include < stdio.h >
#include
< string .h >
int bin[ 2502 ],rank[ 2502 ];
char map[ 55 ][ 55 ];
char mat1[ 100 ][ 100 ]; // 上下连通
char mat2[ 100 ][ 100 ]; // 左右连通
int up[ 8 ],down[ 8 ],left[ 8 ],right[ 8 ];
void prepare()
{
int i,j;
up[
0 ] = ' A ' ; up[ 1 ] = ' B ' ; up[ 2 ] = ' E ' ; up[ 3 ] = ' G ' ; up[ 4 ] = ' H ' ; up[ 5 ] = ' J ' ; up[ 6 ] = ' K ' ;
down[
0 ] = ' C ' ; down[ 1 ] = ' D ' ; down[ 2 ] = ' E ' ; down[ 3 ] = ' H ' ; down[ 4 ] = ' I ' ; down[ 5 ] = ' J ' ; down[ 6 ] = ' K ' ;
left[
0 ] = ' A ' ; left[ 1 ] = ' C ' ; left[ 2 ] = ' F ' ; left[ 3 ] = ' G ' ; left[ 4 ] = ' H ' ; left[ 5 ] = ' I ' ; left[ 6 ] = ' K ' ;
right[
0 ] = ' B ' ; right[ 1 ] = ' D ' ; right[ 2 ] = ' F ' ; right[ 3 ] = ' G ' ; right[ 4 ] = ' I ' ; right[ 5 ] = ' J ' ; right[ 6 ] = ' K ' ;
memset(mat1,
0 , sizeof (mat1));
memset(mat2,
0 , sizeof (mat2));
for (i = 0 ;i < 7 ;i ++ )
{
for (j = 0 ;j < 7 ;j ++ )
{
mat1[down[i]][up[j]]
= 1 ;
mat2[right[i]][left[j]]
= 1 ;
}
}
}

int find( int x)
{
if (x != bin[x])
return bin[x] = find(bin[x]);
return x;
}

void merge( int x, int y)
{
int fx = find(x);
int fy = find(y);
if (fx != fy)
{
if (rank[fx] > rank[fy])
bin[fy]
= fx;
else if (rank[fx] < rank[fy])
bin[fx]
= fy;
else
{
bin[fx]
= fy;
rank[fy]
++ ;
}
}
}

int main()
{
int n,m,i,j;
prepare();
while (scanf( " %d%d " , & n, & m),n + m !=- 2 )
{
getchar();
for (i = 0 ;i < n;i ++ )
gets(map[i]);
for (i = 0 ;i < m * n;i ++ )
bin[i]
= i, rank[i] = 1 ;
for (i = 0 ;i < n;i ++ )
{
for (j = 0 ;j < m;j ++ )
{
if (j < m - 1 && mat2[map[i][j]][map[i][j + 1 ]] == 1 )
merge(i
* m + j,i * m + j + 1 );
if (i < n - 1 && mat1[map[i][j]][map[i + 1 ][j]] == 1 )
merge(i
* m + j,i * m + j + m);
}
}
int con = 0 ;
for (i = 0 ;i < m * n;i ++ )
if (bin[i] == i)
con
++ ;
printf(
" %d\n " ,con);
}
return 0 ;
}

 

HDU1558 Segment set

第一点:如何判断两条线段是否有交点。

第二点:快速查找某条线段所在集合里的线段条数,用并查集实现。

ContractedBlock.gif ExpandedBlockStart.gif 代码
 
   
#include < stdio.h >
#define N 1005
typedef
struct node{
double x,y;
}NODE;
NODE stap[N],endp[N];
int cnt[N],rank[N],bin[N];
int index,n;
void init()
{
index
= 1 ;
for ( int i = 1 ;i <= n;i ++ )
{
bin[i]
= i;
rank[i]
= 1 ;
cnt[i]
= 1 ;
}
}

int Find( int x)
{
if (x != bin[x])
return bin[x] = Find(bin[x]);
return x;
}
void Merge( int x, int y)
{
int fx = Find(x);
int fy = Find(y);
if (fx != fy)
{
if (rank[fx] > rank[fy])
{
bin[fy]
= fx;
cnt[fx]
+= cnt[fy];
}
else
{
if (rank[fx] == rank[fy])
rank[fy]
++ ;
bin[fx]
= fy;
cnt[fy]
+= cnt[fx];
}
}
}

int Value(NODE s,NODE e,NODE t)
{
double ans = (e.x - s.x) * (t.y - s.y) - (t.x - s.x) * (e.y - s.y);
if (ans < 0 )
return - 1 ;
else if (ans > 0 )
return 1 ;
else
return 0 ;
}
int Cross(NODE s1,NODE e1,NODE s2,NODE e2)
{
// if(!(Value(s1,e1,s2)*Value(s1,e1,e2)>0) && !(Value(s2,e2,s1)*Value(s2,e2,e1)>0))
if ((Value(s1,e1,s2) * Value(s1,e1,e2) <= 0 ) && (Value(s2,e2,s1) * Value(s2,e2,e1) <= 0 ))
return 1 ;
return 0 ;
}

void Add(NODE s,NODE e)
{
for ( int i = 1 ;i < index;i ++ )
{
if (Cross(s,e,stap[i],endp[i]))
Merge(i,index);
}
}

int main()
{
int cas,x,fx;
char ch[ 4 ];
scanf(
" %d " , & cas);
while (cas -- )
{
scanf(
" %d " , & n);
init();
while (n -- )
{
scanf(
" %s " ,ch);
if (ch[ 0 ] == ' P ' )
{
scanf(
" %lf%lf%lf%lf " , & stap[index].x, & stap[index].y, & endp[index].x, & endp[index].y);
Add(stap[index],endp[index]);
index
++ ;
}
else {
scanf(
" %d " , & x);
fx
= Find(x);
printf(
" %d\n " ,cnt[fx]);
}
}
if (cas)
puts(
"" );
}
return 0 ;
}

PKU1182 食物链

一组测试实例,写成多组的话会WA,道理同PKU2492一样,用到偏移量

ContractedBlock.gif ExpandedBlockStart.gif 代码
 
   
#include < stdio.h >
#define N 50005
int bin[N],offset[N];
int ans;
int find( int x)
{
if (x != bin[x])
{
int t = find(bin[x]);
offset[x]
= (offset[x] + offset[bin[x]]) % 3 ;
return bin[x] = t;
}
return x;
}
void merge( int d, int x, int y)
{
int fx = find(x);
int fy = find(y);
if (fx == fy)
{
if (d == 1 )
{
if (offset[x] != offset[y])
ans
++ ;
}
else
{
if ((offset[x] - offset[y] + 3 ) % 3 != 1 )
ans
++ ;
}
}
else
{
if (d == 1 ){
bin[fx]
= fy;
offset[fx]
= (offset[y] - offset[x] + 3 ) % 3 ;
}
else {
bin[fx]
= fy;
offset[fx]
= (offset[y] - offset[x] + 4 ) % 3 ;
}
}
}
int main()
{
int n,k,i,d,x,y;
scanf(
" %d%d " , & n, & k);
ans
= 0 ;
for (i = 1 ;i <= n;i ++ )
{
bin[i]
= i;
offset[i]
= 0 ;
}
while (k -- )
{
scanf(
" %d%d%d " , & d, & x, & y);
if (x > n || y > n || (d == 2 && x == y)){
ans
++ ;
continue ;
}
merge(d,x,y);
}
printf(
" %d\n " ,ans);
return 0 ;
}

 

HDU1811 Rank of Tetris

拓扑排序+并查集

如何去理解 拓扑排序算法

转载于:https://www.cnblogs.com/DreamUp/archive/2010/07/19/1780916.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值