并查集——亲戚

//本文首先发表于博客园,点击这里查看我的博客。  
废话不多说,直接看题:

image

image

一看这道题,我就有了思路:既然这道题身在图论板块,那么就要用图的存储、操作方法来解决,先开一个二维数组a[20001][20001],把初值尽可能赋大,再输入数据,并建立关系,然后用floyed算法,虽然不用求最短路径,但是至少能知道两人的关系能否通过中继联通,如果结果正常(即a[i][j]!=999999),则输出“Yes”,否则输出“No”。

代码如下:

<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;">#include<iostream>
using namespace std; int a[20001][20001],n,m,q;int x,y; int main()
{
cin>>n>>m; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++)
{
a[i][j]=999999;//初始化,方便floyed算法
} for(int i=1;i<=n;i++)
a[i][i]=0;//自己和自己当然不是亲戚
for(int i=1;i<=m;i++)
{
cin>>x>>y;
a[x][y]=1;//建立关系
a[y][x]=1;
} for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++)
{ if(a[i][j]>a[i][k]+a[k][j])//floyed算法
a[i][j]=a[i][k]+a[k][j];
}
cin>>q; while(q--)
{
cin>>x>>y; if(a[x][y]!=999999)
cout<<"Yes"<<endl;//有关系
else cout<<"No"<<endl;//无关系
} return 0;
}</pre>

image

可是结果却不容乐观,花式错误:运行超时,运行错误,内存超限。通过这道题我明白了电脑内存有多么脆弱,才410^8个数,我平时十分珍惜空间大小,每一次都开小数组,难得浪一次,竟然爆空间了。我仍不死心,又换成深搜试了试,结果连样例都过不了~~,代码就不奉上了;后来我有恶补比floyed算法快的其他最短路径算法(如dijkstra),发现时间复杂度也好不到哪去。后来回头一看题,竟然关系就有100,0000个,询问有10,0000次,这是让我O(n)做完吗,还要用一维数组*,后来听说要用并查集。

那么并查集是什么?这并查集得分开看每一个字;前两个字“并”和“查”都是并查集的基本操作,稍后自然会说;“集”则代表这些数是一个个集合。“天生我才必有用”,并查集比起数组来说,更节省空间,二维数组10000*10000就爆了,怎能容得下这道题的数据范围呢?而并查集用的只是一维数组;而且并查集比起floyed算法来说,时间复杂度也只有O(n),比floyed O(n^3)好多了。

接下来就以此题为例,介绍一下并查集的基本操作

1)初始化并查集:把每一个数据都先初始成一个单独的集合;

2)查找:不断递归/循环找到指定数据的父节点,直到找到其祖先节点,其中并查集数组每一元素存储的是其父节点编号,祖先节点满足无父节点,也就是其存储值仍为初始化时的值,即其编号等于其存储的值(因为初始化时为了分成不同集合把存储值赋成其编号);祖先节点并非真的是祖先,其实就是一个标志,只要值为同一祖先节点编号的元素,都能判断他们在同一集合中,节省了时间;

3)合并:如果两人祖先不同,且数据给出他们有亲缘关系,就可以将他们合并到同一祖先下;

4)判断元素是否在同一集合中:判断二人祖先是否相同,如果相同,则在同一集合内;否则在不同集合。

废话不多说,为了更好理解,特呈上手写模板,可以与上文结合看:

<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;">#include<iostream>
using namespace std; int a[20001],n,m,x,y,q,x2,y2; void setup()//初始化并查集
{ for(int i=1;i<=n;i++)//共有n个人,每个人都要初始化
{
a[i]=i;//初始化每个人都是独立的集合
}
} int find(int x)//查找祖先
{ if(a[x]==x) return x;//找到祖先立即返回
else return a[x]=find(a[x]);//否则继续寻找+优化(路径压缩)
} void mix(int x,int y)//合并至同一集合
{
x=find(x);y=find(y);//计算两人的祖先
if(x!=y) a[x]=y;//若不相同则将x合并到y的祖先下
} bool is_relative(int x,int y)//判断是否有亲属关系
{ return find(x)==find(y);//查看两人祖先,判断是否相同并返回布尔值以便输出
} int main()
{
cin>>n>>m;
setup();//初始化
for(int i=1;i<=m;i++)
{
cin>>x2>>y2;
mix(x2,y2);//合并到同一集合
}
cin>>q; while(q--)
{
cin>>x2>>y2; if(is_relative(x2,y2)) cout<<"Yes"<<endl;//有关系
else cout<<"No"<<endl;//没关系
} return 0;
}</pre>

要AC代码的同志切勿粘贴,别说我没说过,这只是个模板。小编习惯用cin,cout再加上多次使用函数,会使速度变慢,但是改过之后发现仍不能过,代码就算了,于是又研究并查集的优化方法,提升速度,常规优化方法如下:

1)路径压缩:听到这个词,小编就想路径压缩麻烦吗?会有什么好处?既然压缩,又怎么压缩?不用担心,小编会一一解答。其实小编代码中的find函数已经在用路径压缩了,可以和我下面讲的相结合来看。其实也好理解,下面是小编一时兴起画的,技术不好,见谅。

image

如图所示左图为路径压缩前的亲戚关系图,要确定4和6有无亲戚关系则需要浪费更多的时间;但右图是压缩路径后的亲戚关系图,能很好地表现出各节点到祖先节点的关系,查找次数也由此减少。但路径压缩的弊端也显露出来,这样虽然能表现出各节点和祖先节点的关系,但却破坏了原本的结构,无法判断各节点到父节点的直接关系,在个别题目不能使用。

2)按秩合并:这个优化方法既麻烦又不怎么常见,有两种,按大小合并和按深度合并,大小合并很简单,小的合到大的集合中呗;深度合并则考虑的更多,将关系当成树,把深度小的合并到深度大的上。优点是既能节约时间还能保留好原有关系,但是没有路径压缩快,此题不宜用,也就不详解了(其实是小编不太会),知道思想就行。

优化之后就大功告成了,小编心中尚存一丝侥幸,据说“std::ios::sync_with_stdio(false);可以使cin,cout和scanf,printf速度相差无几”,这是在《信息学奥赛一本通》上看到的,应该挺有用的吧,小编试了一下,切,还相差无几,简直和原来一样,依旧是四个测试点没过超时,最终全部改成scanf,printf就通过了。行了,不说了,代码胜于雄辩,AC代码呈上:

<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;">#include<cstdio>
using namespace std; int a[20001],n,m,sum,p,q; /void setup() //初始化每一个集合
{
for(int i=1;i<=n;i++)
{
a[i]=i;//每一个人都初始化为一个单独集合
}
}
/
int find(int x)//不断寻找父节点+路径压缩
{ if(a[x]==x) return x;//找到尽头,即祖先节点
else return find(a[x]);
} /void mix(int x,int y)//合并各个集合
{
x=find(x);y=find(y);
if(x!=y) a[x]=y;//将x和y合并到同一祖先下
}
/
/bool is_relative(int x,int y)//判断是否同一祖先,是否在同一集合
{
return find(x)==find(y);
}
/
int main()
{
scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)
{
a[i]=i;//每一个人都初始化为一个单独集合
} for(int i=1;i<=m;i++)
{
scanf("%d%d",&p,&q); int r1=find(p); int r2=find(q); if(r1!=r2) a[r2]=r1;
}
scanf("%d",&sum); while(sum--)
{ int x,y;
scanf("%d%d",&x,&y); if(find(x)==find(y)) printf("Yes \n"); else printf("No \n");
} return 0;
}</pre>

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容

  • 在C语言中,五种基本数据类型存储空间长度的排列顺序是: A)char B)char=int<=float C)ch...
    夏天再来阅读 3,320评论 0 2
  • 贪心算法 贪心算法总是作出在当前看来最好的选择。也就是说贪心算法并不从整体最优考虑,它所作出的选择只是在某种意义上...
    fredal阅读 9,195评论 3 52
  • "use strict";function _classCallCheck(e,t){if(!(e instanc...
    久些阅读 2,027评论 0 2
  • 计算机二级C语言上机题库(南开版) 1.m个人的成绩存放在score数组中,请编写函数fun,它的功能是:将低于平...
    MrSunbeam阅读 6,310评论 1 42
  • 《人性的弱点》Day 6@ 因心木灬 如果你辩论、反驳,或许你会得到胜利,可是那胜利是短暂、空虚的………你永远得不...
    小冷睡了阅读 211评论 0 0