您的位置:首页 > 其它

关于并查集的笔记

2015-01-01 21:51 309 查看
下面的代码是 TYVJ p1251 的代码

题目不在赘述 是一个裸的的并查集题目

并查集详细讲解见 http://blog.csdn.net/power721/article/details/4683604(神牛的博客)
#include<cstdio>

#include<cstdlib>

#include<algorithm>

#include<cstring>

using namespace std;

struct S{

int f,rank;

}t[5010];

int ans[5010],n,m,k;

void build(int x){for(int i=1;i<=n;i++){t[i].f=i;t[i].rank=0;}}

int get(int x){int i,q;

for(i=x;t[i].f!=i;i=t[i].f);

for(int j=x;j!=i;j=q){

q=t[j].f;

t[j].f=i;

}

return i;

}

void link(int x,int y){

if(t[x].rank>t[y].rank)t[y].f=x;

else{t[x].f=y;

if(t[x].rank==t[y].rank)

t[y].rank+=1;

}

}

int main()

{

memset(ans,0,sizeof(ans));

scanf("%d%d%d",&n,&m,&k);

build(n);

int x,y;

for(int i=1;i<=m;i++){

scanf("%d%d",&x,&y);

link(get(x),get(y));

}

for(int i=1;i<=k;i++){

scanf("%d%d",&x,&y);

if(get(x)==get(y))ans[i]=1;

}

for(int i=1;i<=k;i++){

if(ans[i])printf("Yes\n");

else printf("No\n");

}

return 0;

}

关于并查集应用的笔记

关于动态的连通性见神牛的博客 http://blog.csdn.net/dm_vincent/article/details/7655764
一下内容来源于网络:

 并查集是一种群众喜闻乐见的数据结构,其复杂度是数据结构中最奇葩的之一了,Tarjan证明其为阿克曼函数的反函数,在可以想象(不全面的解释啊)的范围内小于等于3。。。我们就把它当做O(1)吧。下面通过几道基础的并查集来探查一下并查集的应用。递归调用并查集

  最裸的并查集就只有表示一个集合的功能,支持动态的合并,查询两者是否属于一个集合,这部分内容太简单,请自行Baidu。

  在并查集上可以加入边权,成为加权并查集,一般来说这类题目形式比较单一,纯属个人娱乐吧。。

  加权并查集裸题——银河英雄传说

  这是一道最裸的加权并查集,对于每一个节点维护一个到最老最先的边权值和,再在祖先上维护一个Size,表示集合的大小。Wikioi上的题解有个水表说不能路径压缩。。。怎么可能嘛!!!我们仔细思考一下,每一个点在一个集合中只会被压缩一次,压缩之后就会被连接到祖先上,到祖先的权值和等于到原来的父亲的权值加上原来的父亲到祖先的权值和,只需要在压缩的时候更新一下就好了。合并的时候,把两个集合(不妨设A,B)合并在一起,假如把A并入B,则把A的祖先节点连在B的祖先上,边权设为B的Size。相当于把A集合直接接在B的后面,如此,这个问题就被很好的解决了。

上面这道是裸题,下面有一个看起来不裸的的水题:

  加权并查集弱题——食物链

  初一看这道题似乎和并查集关系不大,但是仔细观察可以发现,不同的个体存在一些关系。我们将物种设为0,1,2三种,则给出的条件分为两种——1.两个个体数字(物种)相同。2,个体A数字=(个体B数字+1)%3 。是否有了一点发现呢,如果是相同的数字,并且不属于同一个集合,就想合并办法让他们之间的权值变为0(mod 3),同理,可以让其数字变为1或2(mod 3),只需要在路径压缩的时候取模就行了。水题解决,代码用下一道的吧。。。

  原创题——魏总数星星

  

魏总数星星

(star.cpp/c/pas)

【描述】

魏总,也就是DP魏,非常喜欢星星,有一天他躺在草坪上数星星。天上共有i颗星星,魏总把天空分成了K个扇形,绕着天空的中心——月亮排布。月亮看见魏总喜欢星星,非常不爽,她就想考一下魏总。月亮给出n队星星的相互关系,形如a b p表示b星星在a星星所在扇形区域的顺时针方向第p个扇形内(0<=p<=k)(p==0时表示在同一个扇形内)。最后月亮要询问m次,形如a b表示询问a b两星是否在一个扇形内,是则输出“Yes”,不是则输出“No”,不知道则输出“Unknown”。由于月亮看魏总喜欢星星变得心情急躁,可能有一些关系与前面的关系矛盾,则这些关系无效。月亮说如果不能把她的所有询问答对就要发出强光,让魏总看不到星星,而本来是大神的魏总因为想见到星星不能编程,只有把这个艰巨的任务交给你了。

【输入】

第一行四个整数i,k,n,m表示i个星星,k个扇形,n个关系,m个询问。

接下来n行,每行三个整数a b p 表示表示b星星在a星星所在扇形区域的顺时针方向第p个扇形内。

接下来m行,每行两个整数a b表示询问a,b是否在同一个扇形内。

【输出】

共m行,每行为“Yes”或“No”或“Unknown”对应每一个询问

【样例输入】

5 5 3 3

1 2 1

2 4 2

4 5 2

1 2

3 4

1 5

【样例输出】

No

Unknown

Yes

【数据范围】

20%,魏总数不超过100个星星,月亮询问不超过100次,天空被分成不超过10个区域。

50%,魏总数不超过4000个星星,月亮询问不超过4000次,天空被分成不超过1000个区域。

100%,魏总数不超过100000个星星,月亮询问不超过100000次,天空被分成不超过10000个区域,关系数少于200000。

其实很容易发现这道题和食物链是一模一样的,只不过把边权值改为了比较大的数,只需稍微改一下代码即可,代码如下:

总结:并查集可以处理没有分离操作的一类问题,可以在极快的时间内处理元素间相互关系的问题,好写好用,就是没什么地方用。。。

下面的内容是http://blog.csdn.net/dm_vincent/article/details/7769159的笔记

首先还是回顾和总结一下关于并查集的几个关键点:

以树作为节点的组织结构,结构的形态很是否采取优化策略有很大关系,未进行优化的树结构可能会是“畸形”树(严重不平衡,头重脚轻,退化成链表等),按尺寸(正规说法叫做秩,后文全部用秩来表示)进行平衡,同时辅以路径压缩后,树结构会高度扁平化。
虽然组织结构比较复杂,数据表示方式却十分简洁,主要采用数组作为其底层数据结构。一般会使用两个数组(parent-link array and size array),分别用来保存当前节点的父亲节点以及当前节点所代表子树的秩。第一个数组(parent-link array)无论是否优化,都需要使用,而第二个数组(size array),在不需要按秩合并优化或者不需要保存子树的秩时,可以不使用。根据应用的不同,可能需要第三个数组来保存其它相关信息,比如HDU-3635中提到的“转移次数”。
主要操作包括两部分,union以及find。union负责对两颗树进行合并,合并的过程中可以根据具体应用的性质选择是否按秩优化。需要注意的是,执行合并操作之前,需要检查待合并的两个节点是否已经存在于同一颗树中,如果两个节点已经在一棵树中了,就没有合并的必要了。这是通过比较两个节点所在树的根节点来实现的,而寻找根节点的功能,自然是由find来完成了。find通过parent-link数组中的信息来找到指定节点的根节点,同样地,也可以根据应用的具体特征,选择是否采用路径压缩这一优化手段。然而在需要保存每个节点代表子树的秩的时候,则无法采用路径压缩,因为这样会破坏掉非根节点的尺寸信息(注意这里的“每个”,一般而言,在按秩合并的时候,需要的信息仅仅是根节点的秩,这时,路径压缩并无影响,路径压缩影响的只是非根节点的秩信息)。

不存在环路(对于有向图,不存在环路也就意味着不存在强连通子图)
满足边数加一等于顶点数的规律(不考虑重边和指向自身的边)

第一条,在并查集中应该如何实现呢?
现在我们对并查集也有一定的认识了,其实很容易我们就能够想出,当两个顶点的根节点相同时,就代表添加了这一条边后会出现环路。这很好解释,如果两个顶点的根节点是相同的,代表这两个顶点已经是连通的了,对于已经连通的两个顶点,再添加一条边,必然会产生环路。
第二条呢?
图中的边数,我们可以在每次进行真正合并操作之前(也就是,在确认两个待合并的顶点的根节点不相同时)进行记录。然后顶点数,也就是整个合并过程中参与进来的顶点个数了,可以使用一个布尔数组来进行记录,出现后将相应位置设为true,最后进行一轮统计即可。

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: