gw_Disjoint Set (并查集) 模板及拓展应用

此文章根据北大暑期课件并查集写的~~详情请看此文档~~~~;

树形结构,合并以及查询都能logn?????反正很快吧~~~~


Merge(b,f); 将f所在树挂为b所在树的直接子树

Query(b,f); 简单比较b和f所在树的根节点是否相同


缺点,树结构可能太深,查询太慢,

方法一:根据树的层次进行合并 ,每个节点(元素)维护一个rank表示子树最大可能高度,较小的rank的树连到较大rank树的根部。~~~此方法略了~~~;

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~我是分割线~~~~~~~~~~~~~~~~~~~~~~~~~~~~

方法二:路径压缩。

为什么rank不用了,因为根据时间复杂度的计算,rank可以省略了,路径压缩足矣。详情看课件

开设父节点指示数组Par,Par[i]代表第i个元素的父亲,若元素i是树根,则Par[i] = i;

a[i]数组用来表示父亲节点是哪一个 通过Query函数 即look函数查询x的祖先节点是哪个~~~

int look(int x)
{
    while(x != a[x])
        x = a[x];
    return x;
}
查询x的祖先并返回


合并merge函数,合并x,y

void merge(int x, int y)
{
    int fx = look(x);//查询x的祖先节点
    int fy = look(y);//查询y的祖先节点
    if(fx != fy){ //如果祖先节点不相同
        a[fx] = fy;//那么合并到一个集合中,此时fx的祖先节点为fy
        cnt[fy] +=cnt[fx];//cnt为统计祖先节点所在集合的数量,cnt[fy]为fy为祖先的集合中元素的个数
    }
}

POJ 1611 The Suspects

代码如下

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <cmath>

using namespace std;
int a[31234];
int cnt[31234];

int look(int x)
{
    while(x != a[x])
        x = a[x];
    return x;
}

void merge(int x, int y)
{
    int fx = look(x);
    int fy = look(y);
    if(fx != fy){
        a[fx] = fy;
        cnt[fy] +=cnt[fx];
    }
}
int main()
{
    int n, m;
    int k;
    int t, c;
    int i, j;
    while(cin >> n >> m){
        if(n == 0 && m == 0)    break;
        for(i = 0;i < n;i++){
            a[i] = i;
            cnt[i] = 1;
        }
        for(i = 0;i < m;i++){
            cin >> k;
            cin >> t;
            for(j = 1;j < k;j++){
                cin >> c;
                merge(t,c);
            }
        }

        cout << cnt[look(0)] << endl;//0为传染源,找到0所在的祖先,输出集合中的数量即可。
    }

    return 0;
}
POJ 1988 Cube stacking

此题的向下求和真是神奇啊~~~~真心佩服想出此方法的大神

题目大意:

题目共有两种操作:

M  x  y 表示把方块x所在的堆,拿起来,叠放到y所在的堆上

C  x     表示方块下面有多少个方块

操作次数p<=100,000次,对每次C操作,输出结果

题解:

除了a数组存储父亲节点以外,还要开设sum数组:记录每一堆一共有多少个方块

若a[i] = a;,则sum[a]表示a所在堆的方块数目。

under数组,under[i]表示第i个方块下面有多少个方块。

under数组在堆合并和路径压缩的时候都要更新。

注意: (此算法的核心)

调用get(4)进行路径压缩的同时,更新under[4],under[4]等于从4到根路径上所有节点的under值之和,

路径压缩的同时,该路径上所有的under值也都更新了

代码如下: 不要用cin输入 否则TLE~~~~~~教训啊~~~~~~~~~

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <cmath>

using namespace std;

const int MAXN = 31234;
int a[MAXN];
int sum[MAXN];//若a[i] =i;sum[i]表示砖块i所在堆的砖块数目
int under[MAXN];//under[i]表示砖块i下面有多少块砖

int get(int x)
{
    if(x != a[x]){ //获取x的根,并把a的父亲节点改为根
        int t = get(a[x]);
        under[x] += under[a[x]]; //更新所有走过的路径,所有路径上的under值都将得到改变
        a[x] = t;//使x的父亲节点为根节点
    }
    return a[x];
}
void merge(int x, int y)
{
    int fx = get(x);
    int fy = get(y);
    if(fx != fy){
        a[fx] = fy;//fx的父亲节点为fy,即把fx压在fy上面
        under[fx] = sum[fy];//把fx压在fy上面之后under[fx]的值即为sum[fy]的值;还有一个比较好玩的就是under[fx]在赋值前一定为0,自行考虑吧,哈啊哈哈~~~~
        sum[fy] += sum[fx];//此时fy那一堆的值应该再加上fx那一堆的值
    }
}
int main()
{
    int p;
    scanf("%d", &p);
    int i;
    for(int i = 1;i <= MAXN;i++){
        sum[i] = 1;
        under[i] = 0;
        a[i] = i;
    }
    char s[5];
    int x, y;
    for(i = 0;i < p;i++){
        scanf("%s", s);
        if(s[0] == 'M'){
            scanf("%d %d", &x, &y);
            merge(x,y);
        }else {
            scanf("%d", &x);
            get(x);//更新under[x]的值,以及所有路径上的值,可能会更新,可能更新好了,就无需更新了啦啦啦啦
            printf("%d\n", under[x]);
        }
    }

    return 0;
}

一下题目有待更新

POJ 1182

POJ 2492

POJ 2524

POJ 1182

POJ 1861

POJ 1703

POJ 2236

POJ 2560

POJ 1456


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值