码迷,mamicode.com
首页 > 其他好文 > 详细

【并查集】并查集

时间:2017-10-09 09:53:39      阅读:207      评论:0      收藏:0      [点我收藏+]

标签:利用   amp   情况   str   union   变量   namespace   操作   nod   

模板

数组版:

int parent[MAX_N];
int rank[MAX_N];

void Init(int n){
	for(int i = 0; i < n; ++i){
		parent[i] = i;
		rank[i] = 0;
	}
}

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

void Union(int x, int y){
	x = Find(x);
	y = Find(y);
	if(x == y)
		return;
	if(rank[x] < rank[y]){
		parent[x] = y;
	} else {
		parent[y] = x;
		if(rank[x] == rank[y]) rank[x]++;
	}
}

结构体版:

struct Node
{
    int rank;
    int parent;
 } node[MAX_N];

void Init(int n){
	for(int i = 0; i < n; ++i){
		node[i].parent = i;
		node[i].rank = 0;
	}
}

int Find(int x){
	if(node[x].parent = x){
		return x;
	} else {
		return Find(node[x].parent);
	}
}

void Union(int x, int y){
	x = Find(x);
	y = Find(y);
	if(x == y)
		return;
	if(node[x].rank < node[y].rank){
		node[x].parent = y;
	} else {
		node[y].parent = x;
		if(node[x].rank == node[y].rank) node[x].rank++;
	}
}

 

例题

例1 poj 1182(带权并查集)

 

解题思路:

  这个题是非常经典的并查集问题。并查集作用:查询a和b是否属于同一组;合并a和b所在的组。注意并查集无法进行分割操作。利用题目中动物之间的相对关系可以建立一个并查集。对每一个元素,把它对父节点的关系用数组rank[i]表示,即relation,作为权值。0:与父节点同类  1:吃父节点  2:被父节点吃。

  路径压缩。为了使查找效率提高,我们需要使树的高度尽可能小,让它只有一层,即在查找的过程中,把树中一条路径上的所有点都连在根节点上,从而加快了查找速度,与此同时,还要更新与此节点有关的其他变量,比如此节点与父节点的权值(父节点变为根节点),该树的高度等等。对于此题来讲,就是要更新节点与父节点的关系,因为此节点的父节点已经变为根节点。那么这里如何推导出此节点与根节点的关系呢。假设此节点为x,其父节点为y,y的父节点为z。则:

               rank[x]   rank[y]    x与z的关系权值
		          0        0         0=(i + j)%3  
		          0        1         1=(i + j)%3  
		          0        2         2=(i + j)%3
		          1        0         1=(i + j)%3 
                  1        1         2=(i + j)%3 
                  1        2         0=(i + j)%3 
                  2        0         2=(i + j)%3 
                  2        1         0=(i + j)%3 
                  2        2         1=(i + j)%3

  

  推导公式:rank[x] = (rank[x] + rank[y]) % 3; 即对于i节点,更新公式为:rank[i] = (rank[i] + rank[parent[i]]) % 3。不过还有更简便的方法:模拟向量的运算x->z = x->y + y->z,所以rank[x] = (rank[x] + rank[y])% 3。对3取模是为了保证结果为0,1,2。

  最后是集合的合并操作。合并操作并不复杂,复杂的是更新集合间的关系(即集合根节点的关系)。这里有大神使用向量的形式来计算更新关系权值,假设需要合并x和y,查询得知x和y的根节点分别为:x_p,y_p,如果将x_p连接到y_p,那么rank[x_p]即为x_p与根节点y_p的关系。x_p->y_p = x_p->x + x->y + y->y_p = (-x->x_p + x->y + y->y_p)。所以更新公式为rank[x_p] = (-rank[x] + type - 1 + rank[y] + 3) % 3。(加上3是为了防止出现负数的情况;对3取模是为了保证结果的取值范围)。type即为输入的num。type为1,x与y同类,type为2,x吃y。 

Solution:from 专注如一

#include <cstdio.h>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAX_N = 50000 + 10;
int parent[MAX_N], rank[MAX_N];

void Init(int n){
    for(int i = 0; i < n; ++i){
        parent[i] = i;
        rank[i] = 0;
    }
}

int Find(int x){
    if(parent[x] == x){
        return x;
    } 
    int y = Find(parent[x]);
    rank[x] = (ran[x] + ran[parent[x]]) % 3;
    return parent[x] = y;
}

int Union(int x, int y, int type){
    x_p = Find(x);
    y_p = Find(y);
    if(x_p == y_p){
        if((rank[x] - rank[y] + 3) % 3 == type - 1)
            return 0;
        return 1;
    }
    parent[x_p] = y_p;
    rank[x_p] = (-rank[x] + type - 1 + rank[y] + 3) % 3;
    return 0;
}

int main(){
    int n, k, res = 0;
    int type, x, y;
    scanf("%d %d", &n, &k);
    Init(n);
    for(int i = 0; i < k; ++i){
        scanf("%d %d %d", &type, &x, &y);
        if(x == y && type == 2)
            ++res;
        else if(x > n || y > n)
            ++res;
        else
            res += Union(type, x, y);
    }
    printf("%d\n", res);
    return 0;
}

 

【并查集】并查集

标签:利用   amp   情况   str   union   变量   namespace   操作   nod   

原文地址:http://www.cnblogs.com/Atanisi/p/7638809.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!