热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

每日算法——并查集的应用

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

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

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

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

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

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

  代码:

 1 #include 
2 #include
3 #include
4 #include
5
6 using namespace std;
7 struct spaceship
8 {
9 int father;
10 int len,maxlen;
11
12 spaceship(){len=0;maxlen=1;}
13 }sp[30010];
14
15 int get(int n)
16 {
17 if (sp[n].father==n) return n;
18 int m=get(sp[n].father);
19 sp[n].len+=sp[sp[n].father].len;
20 sp[n].father=m;
21 return sp[n].father;
22 }
23
24 void marry(int n,int m)
25 {
26 int nn=get(n);
27 int mm=get(m);
28 sp[nn].father=mm;
29 sp[nn].len+=sp[mm].maxlen;
30 sp[mm].maxlen+=sp[nn].maxlen;
31 }
32
33 int main()
34 {
35 int n;
36 scanf("%d",&n);
37 for (int i=1;i<=30000;i++) sp[i].father=i;
38 for (int i=1;i<=n;i++)
39 {
40 char ch;
41 int j,k;
42 char s[5];
43 scanf("%s",s);
44 if (s[0]=='M')
45 {
46 scanf("%d%d",&j,&k);
47 marry(j,k);
48 }
49 else
50 {
51 scanf("%d%d",&j,&k);
52 if (get(j)==get(k))
53 {
54 printf("%d\n",abs(sp[j].len-sp[k].len)-1);
55 }
56 else
57 printf("-1\n");
58 }
59 }
60 return 0;
61 }
View Code

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

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

  初一看这道题似乎和并查集关系不大,但是仔细观察可以发现,不同的个体存在一些关系。我们将物种设为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”。由于月亮看魏总喜欢星星变得心情急躁,可能有一些关系与前面的关系矛盾,则这些关系无效。月亮说如果不能把她的所有询问答对就要发出强光,让魏总看不到星星,而本来是大神的魏总因为想见到星星不能编程,只有把这个艰巨的任务交给你了。

【输入】

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

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

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

【输出】

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

 

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

 

 1 #include 
2 #include
3 #include
4 #include
5
6 using namespace std;
7 struct star
8 {
9 int father;
10 int len;
11
12 star()
13 {
14 father=0;
15 len=0;
16 };
17 }st[100010];
18 int mod;
19 int get(int now)
20 {
21 if (st[now].father==now)
22 {
23 return now;
24 }
25 int f=get(st[now].father);
26 st[now].len=(st[now].len+st[st[now].father].len)%mod;
27 if (st[now].len==0) st[now].len=mod;
28 st[now].father=f;
29 return f;
30 }
31 void marry(int a,int b,int l)
32 {
33 int bf=get(b);
34 int af=get(a);
35 int len=st[a].len+l;
36 len=len % mod;
37 if (l>=st[b].len)
38 {
39 st[bf].father=a;
40 st[bf].len=l-st[b].len;
41 }
42 else
43 {
44 st[bf].father=a;
45 st[bf].len=mod+l-st[b].len;
46 }
47 }
48
49 int main()
50 {
51 freopen ("star.in","r",stdin);
52 freopen ("star.out","w",stdout);
53 int n,m,k;
54 scanf("%d%d%d%d",&n,&mod,&m,&k);
55 for (int i=1;i<=n;i++)
56 {
57 st[i].father=i;
58 }
59 int a,b,p;
60 for (int i=1;i<=m;i++)
61 {
62 scanf("%d%d%d",&a,&b,&p);
63 if (get(a)!=get(b))
64 {
65 marry(a,b,p);
66 }
67 }
68 for (int i=1;i<=k;i++)
69 {
70 scanf("%d%d",&a,&b);
71 if (get(a)!=get(b))
72 {
73 printf("Unknown\n");
74 }
75 else
76 {
77 if (st[a].len%mod==st[b].len%mod) printf("Yes\n");
78 else printf("No\n");
79 }
80 }
81 return 0;
82 }
View Code

 

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

 


推荐阅读
  • DescriptionclickmeSolution套路的状压期望DP题。。。考虑倒退期望:设fi,jrolepresentationstyleposi ... [详细]
  • 题目描述Takuru是一名情报强者,所以他想利用他强大的情报搜集能力来当中间商赚差价。Takuru的计划是让Hinae帮他去市场上买一个商品,然后再以另一个价格卖掉它。Takur ... [详细]
  • 796.[APIO2012]派遣在一个忍者的帮派里,一些忍者们被选中派遣给顾客,然后依据自己的工作获取报偿。在这个帮派里,有一名忍者被称之为Master。 ... [详细]
  • 3.223.28周学习总结中的贪心作业收获及困惑
    本文是对3.223.28周学习总结中的贪心作业进行总结,作者在解题过程中参考了他人的代码,但前提是要先理解题目并有解题思路。作者分享了自己在贪心作业中的收获,同时提到了一道让他困惑的题目,即input details部分引发的疑惑。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文介绍了九度OnlineJudge中的1002题目“Grading”的解决方法。该题目要求设计一个公平的评分过程,将每个考题分配给3个独立的专家,如果他们的评分不一致,则需要请一位裁判做出最终决定。文章详细描述了评分规则,并给出了解决该问题的程序。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 解决Cydia数据库错误:could not open file /var/lib/dpkg/status 的方法
    本文介绍了解决iOS系统中Cydia数据库错误的方法。通过使用苹果电脑上的Impactor工具和NewTerm软件,以及ifunbox工具和终端命令,可以解决该问题。具体步骤包括下载所需工具、连接手机到电脑、安装NewTerm、下载ifunbox并注册Dropbox账号、下载并解压lib.zip文件、将lib文件夹拖入Books文件夹中,并将lib文件夹拷贝到/var/目录下。以上方法适用于已经越狱且出现Cydia数据库错误的iPhone手机。 ... [详细]
  • 动态规划算法的基本步骤及最长递增子序列问题详解
    本文详细介绍了动态规划算法的基本步骤,包括划分阶段、选择状态、决策和状态转移方程,并以最长递增子序列问题为例进行了详细解析。动态规划算法的有效性依赖于问题本身所具有的最优子结构性质和子问题重叠性质。通过将子问题的解保存在一个表中,在以后尽可能多地利用这些子问题的解,从而提高算法的效率。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • 本文介绍了一个题目的解法,通过二分答案来解决问题,但困难在于如何进行检查。文章提供了一种逃逸方式,通过移动最慢的宿管来锁门时跑到更居中的位置,从而使所有合格的寝室都居中。文章还提到可以分开判断两边的情况,并使用前缀和的方式来求出在任意时刻能够到达宿管即将锁门的寝室的人数。最后,文章提到可以改成O(n)的直接枚举来解决问题。 ... [详细]
  • 本文介绍了最长上升子序列问题的一个变种解法,通过记录拐点的位置,将问题拆分为左右两个LIS问题。详细讲解了算法的实现过程,并给出了相应的代码。 ... [详细]
  • 本文讨论了一个数列求和问题,该数列按照一定规律生成。通过观察数列的规律,我们可以得出求解该问题的算法。具体算法为计算前n项i*f[i]的和,其中f[i]表示数列中有i个数字。根据参考的思路,我们可以将算法的时间复杂度控制在O(n),即计算到5e5即可满足1e9的要求。 ... [详细]
  • 刚开始crousera上学习<algorithmspart1>但对JAVA实在是不熟。******************************************** ... [详细]
author-avatar
心语忆录_288
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有