HDU1213 & 并查集的一些学习笔记

来源:互联网 发布:java web快速开发 ide 编辑:程序博客网 时间:2024/05/16 15:08

关于并查集我不是直接接触的,而是通过克鲁斯卡尔算法粗略地学习的……挺惭愧……这里我推荐一个我入门时候看到的博客http://blog.csdn.net/dellaserss/article/details/7724401/点击打开链接

主体结构就是一个一维数组而已,我习惯用pre,数组中的内容是记录每一个点的父亲节点在哪里。

比如,下标为2的元素是4(pre[2]==4),就说明序号为2的点的父亲节点的序号是4.

并查集所要维护的东西是树。

并查集的主要函数有:Find函数,作用是找到这个点的祖宗节点;unite函数,作用是连接两个点所在的树;init函数,初始化pre函数(这个初始化是必要的)。


作为初学者,我对unite函数是有一些讨论的,也就是对合并的方式有所注意,因为有很多种树的合并方式。

一开始unite我写的是这样子:

void unite(int a,int b){    int x=Find(a);    int y=Find(b);        if(x!=y)     pre[x]=y;}

而Find函数就是很朴素的找祖宗

int Find(int x){    int r=x;    while(r!=pre[r]) r=pre[r];    return r;}

并没有做路径的压缩。

在这里我想提一点,如果不是以一定的大小顺序输入相连数据的话,pre[x]=y和pre[y]=x没有很大的实质性的差别,因为谁也不知道x和y谁大,所以当Find函数没有路径压缩时,这两种写法无所谓。

另外一点就是当一个点的父亲节点就是他自己时,这个点就是这棵树的根节点(祖宗节点)。(即pre[r]==r的时候)


我们发现,unite这个函数只是把一棵树A的祖宗节点连到了另一个树B的祖宗节点上,而并没有把A的所有的子节点都直接连到B树的祖宗节点上,这样在寻找A树叶子的祖宗节点时会浪费不少的时间,因为要一个父亲节点一个父亲节点地去找。

我们何不直接把A的所有子节点都直接连到B树的祖宗节点上来呢?(也就是路径压缩)

因为最终是求森林中有几棵树,所以进行上面的操作和最终结果没有差别,而且还能省时间。

要怎么做呢?

在Find函数这里做手脚。

这个Find函数可以进行路径压缩的优化的。

int Find(int x){    if(pre[x]!=x) pre[x]=Find(pre[x]);    else return pre[x];}

我开始不怎么理解,我们可以先把pre[x]=Find(pre[x])中的前面的pre[x]=去掉,这样只要pre[x]!=x那么Find函数会一直找下去,找到根,带着根的值return,这时我们把pre[x]加上去。那么这个函数就顺带着把x的所有的父节点的父节点都变成了根,也就完成了路径压缩的任务。(注意路劲压缩不是在两棵树合并时候进行的,而是在同一棵树上找根时候进行的。)


具体的题目有很多就举一个例子好了HDU 1213,毕竟是并查集裸题。

AC代码:

#include <iostream>#include <cstdio>#include <string>#include <cstring>using namespace std;const int maxn=1e3+10;int N,M;int pre[maxn];void init(){    for(int i=1;i<=N;i++)        pre[i]=i;}int Find(int x){    if(pre[x]!=x) pre[x]=Find(pre[x]);    else return pre[x];}void unite(int a,int b){    int x=Find(a);    int y=Find(b);    pre[y]=x;}int main(){    int T;    scanf("%d",&T);    while(T--)    {        int t1,t2;        scanf("%d%d",&N,&M);        init();        for(int i=1;i<=M;i++)        {            scanf("%d%d",&t1,&t2);            unite(t1,t2);        }        int ans=0;        for(int i=1;i<=N;i++)            if(i==pre[i])                ans++;        printf("%d\n",ans);    }}




原创粉丝点击