公路修建
问题描述
你现在是城市的主人
现在有 n 个村庄,要修建 m 条路,每修建一条路,道路是双向的,输出至少还需要修建几条,可以让所有村庄互相可达。
一开始路为 0 条
数据保证有解
输入格式
第一行两个整数 n,m , 0≤n,m≤105 接下来有 m 行,每行 a,b 代表修建了一条从第 a 个村庄,到第 b 个村庄的路。 1≤a,b≤n
a 和 b 可能相同。
输出格式
一共 m 行,第 i 个数代表已经修建了前 i 条路时,最少还需要修建几条,可以让所有村庄互相可达。
样例输入
5 5
1 1
1 2
2 3
4 4
1 2
样例输出
4
3
2
2
2
并查集!!使用路径压缩
int find(ll x) //寻找所在类的根
{
return bingchaji[x] == x ? x : bingchaji[x] = find(bingchaji[x]);//第二个是返回赋的值
}
后面那项可以把路上经过的 bingchaji[i] 的值全部赋成祖先,之后查询就快了。
算法思想:每次增加一条路的时候,判断是不是把两个类簇合并成一个类簇,如果是就可以减少修一条路
在判断这个图还有几个类簇也用上面的方法,在输入路的时候顺便判断合并了几个类簇。
!!!不要企图用set容器来判断类簇的数量,像这个题目,首先会超时,其次在两个类合成的时候,只有一个根会让他的并查集的值等于另一个,但是这个根的其他儿子的并查集的值还是这个根的值啊,都没有变成另外一个根啊,那不就放到set的时候就出错了吗?!
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll bingchaji[10000000] = {0};
int find(ll x) //寻找所在类的根
{
return bingchaji[x] == x ? x : bingchaji[x] = find(bingchaji[x]);//第二个是返回赋的值
}
int main()
{
ll n, m;
cin >> n >> m;
for (ll i = 1; i <= n; i++) //对并查集初始化
{
bingchaji[i] = i;
}
n--;//最多只需要修n - 1条路
for (ll i = 1; i <= m; i++)
{
ll a, b;
cin >> a >> b;
ll k = find(a);
ll kk = find(b);
if (kk != k)
{
n--;
bingchaji[kk] = k; //更新并查集
//注意这里要把一个类的**根**附属到另一个类的**根**下,如果只把新修的这条路的一个节点归到另一个类的根下面,则这个节点所属的类的其他节点不知道已经属于另一个类了
}
cout << n << endl;
}
return 0;
}