一、算法思想
“边贪心”策略
按边权递增对边排序,
两测试点是否在同一连通块中,
- 不在则将当前边加入最小生成树
- 在则舍弃
直到测试完所有边或者最小生成树生成完毕(边数=n-1)
伪代码:
二、算法实现
由于需要对边权排序,使用邻接表每次都要遍历就不太好了,故直接定义边。
struct edge
{
int u,v;//边的两端点
int cost;//边权
}E[MAXN];
将每个连通块当成一个集合
- 判断两个端点是否在同一个集合中——查
- 合并集合——并
使用“并查集”!
#include <iostream>
#include <algorithm>
#include <vector>
#define MAXN 510
#define MAXDATA 1000000000
using namespace std;
int nv,ne;
struct edge
{
int u,v;//边的两端点
int cost;//边权
}E[MAXN];
bool cmp(edge e1,edge e2){
return e1.cost<e2.cost;
}
int father[MAXN];//并查集数组
int findFather(int x){
//并查集查询函数,路径压缩
if(father[x]<0) return x;
else{
return father[x]=findFather(father[x]);
}
}
int UnionR(int root1,int root2){
//按秩归并,也可以不按秩
if(father[root1]<father[root2]){
father[root1]+=father[root2];
father[root2]=root1;
}else{
father[root2]+=father[root1];
father[root1]=root2;
}
}
int ans=0;
int cur_cnt=0;
int Kruskal(){
//1:Initialize
fill(father,father+nv,-1);
sort(E,E+ne,cmp);
for(int i=0;i<ne;i++){
//检查每条边
int faU=findFather(E[i].u);
int faV=findFather(E[i].v);
if(faU!=faV ||(faU==faV==-1)){//不在同一测试块中
UnionR(faU,faV);//合并进同一测试块中
ans+=E[i].cost;//权加入最小生成树中
cur_cnt++;
if(cur_cnt==(nv-1)) break;//生成完毕
}
}
if(cur_cnt==(nv-1)) return ans;//存在且生成完毕
else return -1;
}
int main(){
scanf("%d %d",&nv,&ne);
for(int i=0;i<ne;i++){
scanf("%d %d %d",&E[i].u,&E[i].v,&E[i].cost);
}
Kruskal();
printf("%d",ans);
return 0;
}
测试数据1:
6 10 0
0 1 4
0 4 1
0 5 2
1 2 6
1 5 3
2 3 6
2 5 5
3 4 4
3 5 5
4 5 3
输出结果1:15
测试数据2:
6 10
0 1 4
0 4 1
0 5 2
1 2 1
1 5 3
2 3 6
2 5 5
3 4 5
3 5 4
4 5 3
测试结果2:11