这题使用并查集来解决,先贴上代码:
#define LOCAL
#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <utility>
using namespace std;
#define MAX_N 1005
int _par[MAX_N]; // 父亲
int _rank[MAX_N]; // 树的高度
// 初始化n个元素
void init(int n)
{
for (int i = 0; i < n; i++)
{
_par[i] = i;
_rank[i] = 0;
}
}
// 查询树的根
int find(int x)
{
if (_par[x] == x)
return x;
else
return _par[x] = find(_par[x]);
}
// 合并x和y所属的集合
void unite(int x, int y)
{
x = find(x);
y = find(y);
if (x == y) return;
if (_rank[x] < _rank[y]) {
_par[x] = y;
}
else {
_par[y] = x;
if (_rank[x] == _rank[y])
_rank[x]++;
}
}
// 判断x和y是否属于同一个集合
bool same(int x, int y)
{
return find(x) == find(y);
}
int main()
{
#ifdef LOCAL
freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);
#endif
int N, M;
while (cin >> N >> M && N) {
for (int i = 0; i < N; ++i)
_par[i] = i;
init(N);
while (M--)
{
int x, y;
cin >> x >> y;
unite(x, y);
}
int num = -1;
for (int j = 1; j <= N; ++j)
if (_par[j] == j)
num++;
cout << num << endl;
}
return 0;
}
这是我之前贴在GitHub上的代码,今天翻以前校赛的题发现了这个就重新拿出来再看一看。并查集的这个代码是我从《挑战程序设计》上面抄下来的,集合了很多并查集的操作,可以直接使用。
======================更新======================
明白了如下代码段的用意:
for (int j = 1; j <= N; ++j)
if (_par[j] == j)
num++;
_par[j]中存储了每个节点的父亲节点,因为并查集的查找操作就是逐层向上找到祖先判断两个节点是否是同一祖先就判断出了是否是属于同一结合,所以如果某个节点的祖先如果是自己,那么说明这是一个联通分量,当联通分量为1的时候,num++结果为0,正好说明不需要修路。所以每增加一个连通分量,就要多修一条路以保证畅通,所以判断j的父节点是否等于j就可以找出连通分量的个数。