HLG 1679 跑步 -- 并查集

链接:http://acm.hrbust.edu.cn/index.php?m=ProblemSet&a=showProblem&problem_id=1679


在解题之前我们先了解一下一些关于并查集的一下基本概念和方法吧;

定义:

并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示。
集就是让每个元素构成一个单元素的集合,也就是按一定顺序将属于同一组的元素所在的集合合并。

主要操作:

初始化----把每个点所在集合初始化为其自身。
通常来说,这个步骤在每次使用该数据结构时只需要执行一次,无论何种实现方式,时间复杂度均为O(N)。
查找----查找元素所在的集合,即根节点。
合并----将两个元素所在的集合合并为一个集合。
通常来说,合并之前,应先判断两个元素是否属于同一集合,这可用上面的“查找”操作实现。

树结构的并查集

 

采用树结构支持并查集的计算能够满足我们的要求。并查集与一般的树结构不同,每个顶点纪录的不是它的子结点,而是将它的父结点记录下来。下面是树结构的并查集的两种运算方式

 

⑴直接在树中查询

⑵边查询边“路径压缩”

 

对应与前面的链式存储结构,树状结构的优势非常明显:编程复杂度低;时间效率高。

 

直接在树中查询

 

集合的合并算法很简单,只要将两棵树的根结点相连即可,这步操作只要O(1)时间复杂度。算法的时间效率取决于集合查找的快慢。而集合的查找效率与树的深度呈线性关系。因此直接查询所需要的时间复杂度平均为O(logN)。但在最坏情况下,树退化成为一条链,使得每一次查询的算法复杂度为O(N)。

 

边查询边“路径压缩

 

其实,我们还能将集合查找的算法复杂度进一步降低:采用“路径压缩”算法。它的想法很简单:在集合的查找过程中顺便将树的深度降低。采用路径压缩后,每一次查询所用的时间复杂度为增长极为缓慢的ackerman函数的反函数——α(x)。对于可以想象到的n,α(n)都是在5之内的。

 

并查集:(union-find sets)是一种简单的用途广泛的集合. 并查集是若干个不相交集合,能够实现较快的合并和判断元素所在集合的操作,应用很多。一般采取树形结构来存储并查集,并利用一个rank数组来存储集合的深度下界,在查找操作时进行路径压缩使后续的查找操作加速。



下面来看一下这道题的代码吧:


#include<iostream>

#include<cstring>

#include<cstdio>

#include<cmath>

#include<algorithm>

using namespace std;

#define clr(x)memset(x,0,sizeof(x))

 

int father[600];

int b[600];

int n, m, i, j, flag;

double res;

 

struct node {

   int x, y;

   double dist;

}e[600*600];

 

int cmp(node a, node b) {  //排序

   return a.dist < b.dist;

}

 

int find(int x) { //查找每个点的父亲节点;

   if(father[x]==x)return x;

   return father[x]=find(father[x]);

}

bool join(int x,int y) { //集合的合并实现

   x=find(x);

   y=find(y);

   if(x==y)return false;

   if(x>y){father[x]=y;b[y]+=b[x];if(b[y]>=m&&flag==0){flag=1;res=e[i].dist;printf("%.4lf\n",res);return0;}}

   if(x<y){father[y]=x;b[x]+=b[y];if(b[x]>=m&&flag==0){flag=1;res=e[i].dist;printf("%.4lf\n",res);return0;}}

   return true;

}

 

int x[600]; //x, y 用来储存每个点的横坐标和纵坐标;

int y[600];

 

int main() {

   int n,T;

   scanf("%d", &T);

   while(T--) {

           flag = 0;

           int tot = 0;

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

           for(i=0; i<n; i++) { //输入坐标

                scanf("%d%d",&x[i], &y[i]);

           }

            for(i=0; i<n; i++) {

                for(j=i+1; j<n; j++) { //将每一点的坐标到除自身以外所有的点的距离;

                    e[tot].x = i;

                    e[tot].y = j;

                    e[tot++].dist =sqrt((double)(x[i]-x[j])*(x[i]-x[j])+(double)(y[i]-y[j])*(y[i]-y[j]));

                }

           }

           for(i=0; i<=n; i++) { // 初始化

                father[i] = i;

                b[i] = 1;

           }

           sort(e, e+tot, cmp); //对路程进行排序

           for(i=0; i<tot; i++) {

                join(e[i].x,e[i].y); //集合的合并

                if(flag == 1) break;

           }

    }

   return 0;

}

欢迎交流;


你可能感兴趣的:(查找,合并,初始化,并查集,路径压缩)