[HDU1232]畅通工程(并查集)

题目

给出城市的数量n以及道路的数量m,城市编号1~n,然后给出m个道路,输入1 2就是1、2之间修一条路,注意可以自身与自身连一条路。求还需要至少多少条路才能使道路畅通,即从任意一个城市出发可以到任何城市。

思路

第一次做并查集,看博客大体知道并查集的含义。所谓并查集其实是一种数据结构,一个集合,ADT分为两个操作。首先我们开一个数组pre[], 根据名字可以知道pre[i] == j的含义是i的前一个数是j。这样我们可以根据任意一个结点找到他的根部,根据两个数的根是否相同判断这两个数是否在同一个集合之中。
让我们开始探索吧!

int find(int x) {
    int r = x;
    while(pre[r] != r)   //1.寻找路径的起点
        r = pre[r];
    int i = x, j;
    while(i != r) {      //2.压缩路径
        j = pre[i];
        pre[i] = r;
        i = j;
    }
    return r;            
}

1.find函数的最主要的任务就是寻找路径的起点,所以用循环1来不断寻找前一个结点直到其前一个结点是自身为止。
2.压缩路径,其实这是一种优化,在本题中不压缩路径也可以过,因为数据少。压缩路径的好处是使所用结点直接指向根减少以后find时的循环1的次数。

void join(int x, int y) 
{
    int fx = find(x);
    int fy = find(y);
    if (fx != fy) {
        pre[fx] = fy;

    }
}

join函数起的作用是将两个结点所在集合进行合并。其实看的还是所谓的根合并也是把两个集合的根之间的关系用pre数组联系起来,十分巧妙。

总结

并查集真是一种十分巧妙的数据结构值得仔细推敲。

代码

#include <cstdio>
#include <cstring>

const int maxn = 1e3 + 10;

int pre[maxn];
int ans;

int find(int x) {          //找到路径的起点
    int r = x;
    while(pre[r] != r) {
        r = pre[r];
    }
    int i = x, j;
    while(i != r) {         //路径压缩
        j = pre[i];
        pre[i] = r;
        i = j;
    }
    return r;
}

void join(int x, int y) {
    int fx = find(x);
    int fy = find(y);
    if (fx != fy) {
        pre[fx] = fy;
        ans--;
    }
}

int main() {
    //freopen("input.txt", "r", stdin);
    int n, m;
    int x, y;
    while(~scanf("%d", &n) && n) {
        scanf("%d", &m);
        ans = n - 1;       //一开始假设都不想通需要建设n-1条
        for(int i = 1; i <= n; i++)
            pre[i] = i;
        while(m--) {
            scanf("%d%d", &x, &y);
            join(x, y);
        }
        printf("%d\n", ans);

    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/sunmaoxiang/article/details/80721080