hdu 1232, disjoint set, linked list vs. rooted tree, a minor but substantial optimization for path c

three version are provided.

disjoint set, linked list version with weighted-union heuristic, rooted tree version with rank by union and path compression, and a minor but substantial optimization for path compression version FindSet to avoid redundancy so to be more efficient. (31 ms to 15 ms)

reference:

1. Thomas H. Cormen, Introduction to Algorithms

2. Disjoint-set Data Structures By vlad_D– TopCoder Member https://www.topcoder.com/community/data-science/data-science-tutorials/disjoint-set-data-structures/

in linked list version with weighted-union heuristic, with a extra tail member in struct myNode to speedup union, find is O(1), simply the p->head, so I remove find() and just used p->head as the find function.

(one main point) every time a list become no longer list, change its head’s num from 1 to 0, thus facilitate the afterwards count process – all node’s num is simply 0 except the ones as the head of linked lists. – similar process in the rooted tree version.

// linked list version with weighted-union heuristic, 15 ms

#include <cstdio>
#include <cstring>
#include <algorithm>

#define MAXSIZE 1005
struct myNode {
    int num;
    myNode *head;
    myNode *next;
    myNode *tail;
};

void MergeSet(myNode *p1, myNode *p2) {
    p1=p1->head, p2=p2->head;
    if(p1->num<p2->num) { std::swap(p1,p2); }
    p1->num+=p2->num, p2->num=0;
    p1->tail->next=p2, p1->tail=p2->tail;
    for(;p2;p2=p2->next) { p2->head=p1; }
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
#endif
    int T, n,m,u,v,i,cnt;
    myNode cities[MAXSIZE], *p,*pend, *q;
    while(scanf("%d%d",&n,&m)==2 && n>0) {
        for(p=&cities[1],pend=p+n;p!=pend;++p) { p->num=1; p->head=p; p->next=0; p->tail=p; }
        for(i=0;i<m;++i) {
            scanf("%d%d",&u,&v);
            if(cities[u].head!=cities[v].head) { MergeSet(&cities[u],&cities[v]); }
        }
        for(cnt=-1, p=&cities[1],pend=p+n;p!=pend;++p) {
            if(p->num>0) ++cnt;
        }
        printf("%d\n",cnt);
    }
    return 0;
}

// rooted tree version, with rank by union and path compression, 31 ms

#include <cstdio>
#include <cstring>
#include <algorithm>

#define MAXSIZE 1005
struct myNode {
    int rank;
    myNode *parent;
};

myNode* FindSet(myNode *p1) {
    if(p1->parent==p1) return p1;
    return p1->parent=FindSet(p1->parent);
}

void Link(myNode *p1, myNode *p2) {
    if(p1->rank<p2->rank) std::swap(p1,p2);
    p2->parent=p1;
    p2->rank=0;
    if(p1->rank==p2->rank) ++p1->rank;
}

void MergeSet(myNode *p1, myNode *p2) {
    Link(FindSet(p1),FindSet(p2));
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
#endif
    int T, n,m,u,v,i,cnt;
    myNode cities[MAXSIZE], *p,*pend, *q;
    while(scanf("%d%d",&n,&m)==2 && n>0) {
        for(p=&cities[1],pend=p+n;p!=pend;++p) { p->rank=1; p->parent=p; }
        for(i=0;i<m;++i) {
            scanf("%d%d",&u,&v);
            if(FindSet(&cities[u])!=FindSet(&cities[v]))
                MergeSet(&cities[u],&cities[v]);
        }
        for(cnt=-1, p=&cities[1],pend=p+n;p!=pend;++p) {
            if(p->rank>0) ++cnt;
        }
        printf("%d\n",cnt);
    }
    return 0;
}

note that in version 2, the path compression FindSet is a two pass recursive function, first pass up to find parent and then pass down return it to update p->parent, thus achieve path compression.

(another main point) But, note that, even when p->parent is the representative, there is no need to update p->parent, FindSet still obliviously call FindSet(p->parent) and assign it to p->parent, which does nothing useful. We can remove this redundancy by a simple modification, in FindSet, replace

if(p1->parent==p1) return p1;

with

if(p1->parent==p1->parent->parent) return p1->parent;

// almost same with version 2, with the optimization just mentioned, 15 ms

#include <cstdio>
#include <cstring>
#include <algorithm>

#define MAXSIZE 1005
struct myNode {
    int rank;
    myNode *parent;
};

myNode* FindSet(myNode *p1) {
    if(p1->parent==p1->parent->parent) return p1->parent;
    return p1->parent=FindSet(p1->parent);
}

void Link(myNode *p1, myNode *p2) {
    if(p1->rank<p2->rank) std::swap(p1,p2);
    p2->parent=p1;
    p2->rank=0;
    if(p1->rank==p2->rank) ++p1->rank;
}

void MergeSet(myNode *p1, myNode *p2) {
    Link(FindSet(p1),FindSet(p2));
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
#endif
    int T, n,m,u,v,i,cnt;
    myNode cities[MAXSIZE], *p,*pend, *q;
    while(scanf("%d%d",&n,&m)==2 && n>0) {
        for(p=&cities[1],pend=p+n;p!=pend;++p) { p->rank=1; p->parent=p; }
        for(i=0;i<m;++i) {
            scanf("%d%d",&u,&v);
            if(FindSet(&cities[u])!=FindSet(&cities[v]))
                MergeSet(&cities[u],&cities[v]);
        }
        for(cnt=-1, p=&cities[1],pend=p+n;p!=pend;++p) {
            if(p->rank>0) ++cnt;
        }
        printf("%d\n",cnt);
    }
    return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-25 01:57:24

hdu 1232, disjoint set, linked list vs. rooted tree, a minor but substantial optimization for path c的相关文章

HDU 1232(畅通工程)题解

以防万一,题目原文和链接均附在文末.那么先是题目分析: [一句话题意] 给定一具有N个节点的图和其边集,求其集合数量. [题目分析] 并查集经典题...其实就是创建好并查集就行了.. [算法流程] 于是这里就是放并查集的基本内容的..用一个数组的下标来对应节点,值来对应其父节点,并查集英文是Disjoint Sets,就叫DJSet好了XD 1 int getDjSetPar(int id) { //取得某节点的父节点 2 if (dj[id]==id) return id; 3 else re

HDU 1232:畅通问题(并查集)

畅通工程 Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 29362    Accepted Submission(s): 15452 Problem Description 某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇.省政府"畅通工程"的目标是使全省任何两个城镇间都可以实现交通(但不一定有

HDU 1232 畅通工程(并查集)

畅通工程 Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 30485    Accepted Submission(s): 16013 Problem Description 某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇.省政府"畅通工程"的目标是使全省任何两个城镇间都可以实现交通(但不一定有

HDU - 1232 畅通工程 HDU - 1874畅通工程续 HDU - 1875畅通工程再续

题目来源:http://acm.hdu.edu.cn/showproblem.php?pid=1232 题目来源:http://acm.hdu.edu.cn/showproblem.php?pid=1874 题目来源:http://acm.hdu.edu.cn/showproblem.php?pid=1875 畅通工程属于水题,直接用并查集就行,加个路径压缩那就更好. 畅通工程续这道题WA了N次,因为有一个地方没注意到就是一个城镇到另外一个城镇的有多条道路, 所以你一开始就要把最短的道路选出来.

hdu 1232 畅通工程(最小生成树)

Problem Description 某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇.省政府“畅通工程”的目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要互相间接通过道路可达即可).问最少还需要建设多少条道路? Input 测试输入包含若干测试用例.每个测试用例的第1行给出两个正整数,分别是城镇数目N ( < 1000 )和道路数目M:随后的M行对应M条道路,每行给出一对正整数,分别是该条道路直接连通的两个城镇的编号.为简单起见,城镇从1

HDU 1754 I hate it 分段树Segment Tree题解

给出一段数据,然后要更新单个数据,会询问一段数据中的最大值. 又是一道分段树操作.渐渐熟手了. #pragma once #include <cstdio> #include <algorithm> using namespace std; class IHateIt_1754_1 { static const int SIZE = 200001; int *segTree; //不要使用segTree[SIZE<<2] inline int lChild(int r)

hdu 1232 畅通工程(并查集算法)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1232 畅通工程 Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 31088    Accepted Submission(s): 16354 Problem Description 某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条

HDU 1232: 畅通工程(in Java)

/** * @link http://acm.hdu.edu.cn/showproblem.php?pid=1232 * @author Sycamore * @date Aug, 18 */import java.util.*; class Main{ static int []parent=new int[1000]; static int find(int x) { return parent[x]=x==parent[x]?x:find(parent[x]); } static void

并查集 -- HDU 1232 UVALA 3644

并查集: 1 int pa[maxn],Rank[maxn]; 2 ///初始化 x 集合 3 void make_set(int x) 4 { 5 pa[x]=x; 6 Rank[x]=0; 7 } 8 ///递归查找 x 所在的集合 9 int find_set(int x) 10 { 11 if(pa[x]!=x) pa[x]=find_set(pa[x]); 12 return pa[x]; 13 } 14 ///更新根节点,如果不更新可能会暴栈 15 void mix(int x,in