现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本,求使每个村落都有公路连通所需要的最低成本。
输入格式:
输入数据包括城镇数目正整数N(≤1000)和候选道路数目M(≤3N);随后的M行对应M条道路,每行给出3个正整数,分别是该条道路直接连通的两个城镇的编号以及该道路改建的预算成本。为简单起见,城镇从1到N编号。
输出格式:
输出村村通需要的最低成本。如果输入数据不足以保证畅通,则输出−1,表示需要建设更多公路。
输入样例:
6 15
1 2 5
1 3 3
1 4 7
1 5 4
1 6 2
2 3 4
2 4 6
2 5 2
2 6 6
3 4 6
3 5 1
3 6 1
4 5 10
4 6 8
5 6 3输出样例:
12
思路分析:
此题需最小生成树得到最小花费,利用Kruskal算法的并查集实现,其中使用路径压缩与按秩合并提高性能。
Kruskal: 核心思想对边按权重进行排序,按权重从小到大顺序取边来连通各分支,如果形成环(连通的点出现在同一并查集里)则舍去该边,直到所有点连通
!啊喂别被什么路径压缩,按秩合并高级到了,理解一下就行
便于理解视频并查集及其相关操作视频Union Find - Union and Find Operations
以下为chatgpt生成范例:
顶点:0, 1, 2, 3, 4
边和权重:
边 0-1,权重 1
边 1-2,权重 2
边 2-3,权重 3
边 3-4,权重 4
边 0-4,权重 5初始状态
parent
数组:[0, 1, 2, 3, 4] (每个顶点初始指向自己)rank
数组:[0, 0, 0, 0, 0] (每个集合的秩初始为 0)按权重排序的边
- 顺序:0-1, 1-2, 2-3, 3-4, 0-4
遍历每条边
边 0-1 (权重 1):
- 顶点 0 和 1 在不同的集合。
- 合并这两个顶点。
parent
数组:[0, 0, 2, 3, 4]。rank
数组:[1, 0, 0, 0, 0],因为合并两个秩为 0 的集合,新集合的秩变为 1。边 1-2 (权重 2):
- 顶点 1 和 2 在不同的集合。
- 合并这两个顶点。
parent
数组:[0, 0, 0, 3, 4]。rank
数组:[1, 0, 0, 0, 0],1-2 合并不会改变秩,因为顶点 1 已经是顶点 0 的子节点。边 2-3 (权重 3):
- 顶点 2 和 3 在不同的集合。
- 合并这两个顶点。
parent
数组:[0, 0, 0, 0, 4]。rank
数组:[1, 0, 0, 0, 0],同上,2-3 合并不会改变秩。边 3-4 (权重 4):
- 顶点 3 和 4 在不同的集合。
- 合并这两个顶点。
parent
数组:[0, 0, 0, 0, 0]。rank
数组:[1, 0, 0, 0, 0],同上,3-4 合并不会改变秩。边 0-4 (权重 5):
- 顶点 0 和 4 现在在同一个集合,忽略这条边。
结果
- 选择了边 0-1, 1-2, 2-3, 和 3-4 构建最小生成树,总权重为 1 + 2 + 3 + 4 = 10。
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int Max= 1000;
struct edge{
int u,v,w;
bool operator<(const edge &e)const{
return w < e.w; //sort函数排序会用到
}
}e[3*Max];
int n,m;
int a[Max+1];
int ranks[Max+1]; //rank会引起歧义哦
int cost,num; //cost->总花费 num->插入边数计数,树种边数=点数-1
void init(int a[]){
for(int i = 1;i <= n;i ++)
a[i] = i; //初始化每个点为一个集
}
int find(int i){
if(i!=a[i])
a[i]=find(a[i]);
return a[i]; //压缩路径并查集,递归保持代码简洁性规避错误
//同一个连通集选取一个点表示集合
}
void unite(int i, int j){
i=find(i);
j=find(j);
if(i!=j){
if(ranks[i]<ranks[j])
swap(i,j); //保持小集合并到大集合中
a[j]=i;
if(ranks[i]==ranks[j])
ranks[i]++;
}
}
int main(){
cin>>n>>m;
init(a);
for(int i = 0;i < m; i ++){
cin>>e[i].u>>e[i].v>>e[i].w; //输入
}
sort(e,e + m); //排序
for(int i = 0;i < m;i ++){
if(find(e[i].u)!=find(e[i].v)){ //加边,如果不在同一集里,合并集
cost += e[i].w;
unite(e[i].u,e[i].v);
num++;
}
}
if(num != n - 1)cout<< -1;
else cout<<cost;
return 0;
}