题目
给出城市的数量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;
}