关闭

并查集(按秩合并)

193人阅读 评论(0) 收藏 举报
分类:

并查集-按秩合并

题目:UVA-11354

题目大意:给出一张n个点m条边的无向图, 每条边有一个危险度,有q个询问, 每次给出两个点s、t,找一条路, 使得路径上的最大危险度最小。

思路:首先,我们可以发现,如果求一个最小生成树, 那么任意两点, 在生成树上有唯一路径, 而且这条路径上的最大危险值一定最小。 但是n和q都太大, 如果直接顺着树走,每次询问最大复杂度O(n), 那么复杂度高达O(n^2),会超时。 我们知道, 并查集在用了路径压缩之后效率高达O(n), 但是却破坏了树形结构, 所以不能用路径压缩。 然而我们经常忽视了按秩合并这个方法, 即使不用路径压缩, 仅仅靠按秩合并, 复杂度也可低至O(logn)。 因此我们只需按秩合并, 然后询问的时候向根回溯就行了, 复杂度mlogn。

注意秩的意思就是树的高度,按秩合并过后并查集的结构为树形结构

样例:

4 5
1 2 10
1 3 20
1 4 100
2 4 30
3 4 10
2
1 4
4 1

此样例按秩合并过后的结构如下:

这里写图片描述

可以看出来按秩合并过后,此并查集呈现出树形的结构,并且呈递增,所此每次访问的肯定是从小到大的线路,因此路径上的最大危险度肯定是最小的。。。

图手画的,不是很好看,还请见谅。。。。。。。

代码如下:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cstdio>
#include <cmath>
#include <vector>

using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
int n,m;
int pre[50000+10],ra[50000+10],vis[50000+10],e[50000+10];
struct node
{
    int u,v,w;
}p[100000+10];
bool cmp(node x,node y)
{
    return x.w<y.w;
}
int Find(int x)
{
    return pre[x]==x?x:Find(pre[x]);
}
void Union(int a,int b,int c)
{
    int x=Find(a);
    int y=Find(b);
    if(x==y)
        return ;
    if(ra[x]<ra[y])
    {
        pre[x]=y;
        e[x]=c;//e数组记录路径上的大小
    }
    else
    {
        pre[y]=x;
        e[y]=c;
        if(ra[x]==ra[y])
            ra[x]++;//ra数组就是记录树的高度,也就是所谓的秩
    }
}
int qurey(int x,int y)//查询
{
    int ans1=0,ans2=-1;
    int cnt=x;//先从x往y查找,
    while(true)
    {
        vis[cnt]=ans1;
        if(cnt==pre[cnt])
            break;
        ans1=max(ans1,e[cnt]);
        cnt=pre[cnt];
    }
    cnt=y;
    while(true)
    {
        if(vis[cnt]>=0)//如果刚刚x在y下面,那么就能扫到y,这里判断一下是的话就可以直接返回了,
        {
            ans2=max(ans2,vis[cnt]);
            break;
        }
        if(cnt==pre[cnt])
            break;
        ans2=max(ans2,e[cnt]);
        cnt=pre[cnt];
    }
    cnt=x;
    while(true)//如果x是在y上面,那刚刚x以上的树节点的vis值就被改变了,需要复原,以便下一次的查询,
    {
        vis[cnt]=-1;
        if(cnt==pre[cnt])
            break;
        cnt=pre[cnt];
    }
    return ans2;
}
void init()
{
    sort(p,p+m,cmp);
    for(int i=1;i<=n;i++)
    {
        pre[i]=i;
        ra[i]=0;
        vis[i]=-1;
    }
    for(int i=0;i<m;i++)
    {
        Union(p[i].u,p[i].v,p[i].w);
    }
    //cout<<"pre[4]="<<pre[4]<<"  "<<"pre[3]="<<pre[3]<<endl;
}
int main()
{
    int kase=0;
    while(cin>>n>>m)
    {
        if(kase)
            cout<<endl;
        else
            kase++;
        for(int i=0;i<m;i++)
            cin>>p[i].u>>p[i].v>>p[i].w;
        init();
        int q;
        cin>>q;
        for(int i=0;i<q;i++)
        {
            int u,v;
            cin>>u>>v;
            cout<<qurey(u,v)<<endl;
        }
    }
    return 0;
}
1
0
查看评论

UVA 11354 - Bond(并查集-按秩合并)

题目链接:点击打开链接 题意:给出一张n个点m条边的无向图, 每条边有一个危险度,有q个询问, 每次给出两个点s、t,找一条路, 使得路径上的最大危险度最小。 思路:首先,我们可以发现,如果求一个最小生成树, 那么任意两点, 在生成树上有唯一路径, 而且这条路径上的最大危险值一定最小。 但是n和...
  • weizhuwyzc000
  • weizhuwyzc000
  • 2016-02-15 16:14
  • 2168

并查集-按秩合并

1239: 中山学院 ACM小组 Time Limit: 1 Sec  Memory Limit: 64 MB Submit: 79  Solved: 21 [Submit][Status][Web Board...
  • qq_24489717
  • qq_24489717
  • 2015-06-16 21:40
  • 1095

JZOJ4769 【GDOI2017模拟9.9】graph CDQ分治+用按秩合并维护带撤销的并查集(BZOJ 4025)

题目大意现在有NN个点,MM个操作,对于每个操作有两种情况 1. Ord=1Ord = 1:读入u,vu,v表示连接u,vu,v。 2. Ord=0Ord = 0:读入uu表示删除第uu次连接的边。 对于每次操作,如果当前是一幅二分图则输出YESYES,否则输出NONO。N,M≤3∗105N,...
  • YxuanwKeith
  • YxuanwKeith
  • 2016-09-10 22:12
  • 1259

并查集(按秩合并、路径压缩)

算法分类: 数据结构 算法原理: 通过find函数找出该节点的根节点, 通过UNION函数将两棵树合并。 加入rank[N]来记录每个节点的秩(即树的高度),并按秩进行合并,可避免合并时的最糟糕情况,(树形为一条直线) 通过路径压缩可以减少每次寻找根节点的次数。 算法时...
  • jokes000
  • jokes000
  • 2012-04-30 11:15
  • 8602

并查集的优化:按秩合并和路径压缩

并查集有两个优化。 一、按秩合并 描述:就是在对两个不同子集连接时,按照rank来连,也就是rank低的连在rank高的下面。rank高的做父亲节点。 作用,这样类似维护了一棵树,树是rank高的在上。 // 初始化n个元素 void init(int n) {    ...
  • disparity_CJK
  • disparity_CJK
  • 2016-08-31 14:32
  • 3349

历史 (并查集 按秩合并)

历史(history.c/cpp/pas)题目描述 历史学家小A正在研究一个奇怪的王国的历史。当前阶段的任务是研究该国的交通。 根据这个奇怪的王国的史书记载,史书开始记载前这个王国有n个城市(城市从0开始标号),但所有城市之间都没有道路相连。 每一年,在位的国王会修建一条x到y的双向道路,一条...
  • w4149
  • w4149
  • 2017-08-28 15:54
  • 274

bzoj 4668(并查集按秩合并)

传送门 题意: 两种操作,强制在线。 1.将u,v连边 2.询问u,v最早什么时候已经连通 题解: 每次连边就更新时间戳赋成每个点的点权,查询链上最大值,所以不能压缩路径(压了路径无法解决分叉的情况),为了保证复杂度,采用按秩合并(复杂度mlogm?)。 #include ...
  • KGV093
  • KGV093
  • 2017-10-13 08:36
  • 96

并查集(两个优化—按秩合并、路径压缩) poj2492

并查集有两个优化。 一、按秩合并 描述:就是在对两个不同子集连接时,按照rank来连,也就是rank低的连在rank高的下面。rank高的做父亲节点。 作用,这样类似维护了一棵树,树是rank高的在上。 二、路径压缩 描述:假如fa数组已经嵌套了N层,那么传统的做法去找祖先要做N次,当N很...
  • u013694972
  • u013694972
  • 2014-04-20 23:00
  • 1217

按秩合并并查集

int findx(int x) { int r=x; while(parent[r] !=r) r=parent[r]; return r; } // 初始化n个元素 void init(int n) {     for(int i=0;i&...
  • zjy2015302395
  • zjy2015302395
  • 2017-01-22 23:57
  • 273

并查集的两种优化(按秩合并,路径压缩)

并查集是建立在对不相交集合进行的两种基本操作的基础之上的。操作之一:检索某元素属于哪个集合;操作之二:合并两个集合。黑书上说了,这种结构显然可以用链表或森林实现,显然用链表进行查询时间复杂度应该是O(n)级别的,而使用森林进行查访如果处理的好时间复杂度就应该是O(logn)。对于用森林来实现并查集,...
  • u010255534
  • u010255534
  • 2013-08-04 22:14
  • 1435
    个人资料
    • 访问:13881次
    • 积分:884
    • 等级:
    • 排名:千里之外
    • 原创:76篇
    • 转载:2篇
    • 译文:0篇
    • 评论:3条