题解:HDU 1232: 畅通工程 (并查集)

来源:互联网 发布:网络拍卖许可证 编辑:程序博客网 时间:2024/05/17 07:38

上题目

Problem Description
某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇。省政府“畅通工程”的目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要互相间接通过道路可达即可)。问最少还需要建设多少条道路?

Input 测试输入包含若干测试用例。每个测试用例的第1行给出两个正整数,分别是城镇数目N ( < 1000
)和道路数目M;随后的M行对应M条道路,每行给出一对正整数,分别是该条道路直接连通的两个城镇的编号。为简单起见,城镇从1到N编号。
注意:两个城市之间可以有多条道路相通,也就是说 3 3 1 2 1 2 2 1 这种输入也是合法的 当N为0时,输入结束,该用例不被处理。

Output 对每个测试用例,在1行里输出最少还需要建设的道路数目。

Sample Input
4 2
1 3
4 3
3 3
1 2
1 3
2 3
5 2
1 2
3 5
999 0
0

Sample Output
1
0
2
998

分析

基础的联通问题

看两个城市是否联通在一起

这里我们运用一种并查集的思想

我们设立一个标志节点 如果两个节点都可以上诉到同一个标志节点 那么这两个节点是联通的

这里用函数说明

全局变量部分

int father[1050];//father[i]所储存的数是father[i]的父节点的坐标int rank[1050]; // 表示这个数的在树中的深度

接下来是初始化的函数

我们先将每个数的父节点设置为他自己也就是每个数是独立的

将他的rank设置为0 代表深度为0; 函数

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

接下来是建立并查集的过程

这里本质上是合并两个集合的过程

比如 1和2联通 我们实际上就是合并1和2的最终父节点

注意这里在合并的过程中实际考虑了高度的问题

优先合并到较高的那一列

这样不会增加并查集的深度

void unite (int x, int y){    x = find(x);    y = find(y);    if(x == y)        return;    if(rank[x] < rank[y])    {        father[x] = y;;    }     else    {        father[y] = x;        if(rank[x] == rank[y])            rank[x]++;    }}

接下来就是查找的查找的过程

可以这样理解

因为father[i]存储的是i的父节点 所以我们可以一直向上遍历

直到出现 father[i] = i 的情况

也就是他的父节点是他自己

这时他就是最终的父节点

这里先上递推版的

int find(int x){    int far = x;    while (far != father[far])// 寻找最终父节点        far = father[far];    return far;}

路径压缩问题

这里我们的做法是将全部的子节点合并到最终父节点下

int minn(int x){    int far = find(x);    int i = x;    while (father[i] != far)// 状态压缩 将各级子节点压缩到最终父节点下    {        int temp = father[i];        father[i] = far;        i = temp;    }    return far;}

上代码+ 注释

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;int father[1050];int t[1050];int find(int x){    int far = x;    while (far != father[far])// 寻找最终父节点        far = father[far];    int i = x;    while (father[i] != far)// 状态压缩 将各级子节点压缩到最终父节点下    {        int temp = father[i];        father[i] = far;        i = temp;    }    return far;}void mix(int x, int y){    int fx = find(x);    int fy = find(y);    if (fx != fy)    {        father[fy] = fx;    }}int main(void){    int N, M, a, b, i, j, ans;    while (scanf("%d%d", &N, &M) && N)    {        for (i = 1; i <= N; i++)          //初始化               father[i] = i;        for (i = 1; i <= M; i++)          //吸收并整理数据           {            scanf("%d%d", &a, &b);            mix(a, b);        }        memset(t, 0, sizeof(t));        for (i = 1; i <= N; i++)          //标记根结点          {            t[find(i)] = 1;        }        for (ans = 0, i = 1; i <= N; i++)            if (t[i])                ans++;        printf("%d\n", ans - 1);    }    return 0;}
0 0