问题描述
求解连通图的最小生成树问题
输入格式
输入的第一行包含 N N N和 M M M两个整数。其中, N N N为连通图中点的数量, M M M为连通图中边的数量。所有点的编号从 0 0 0到 N − 1 N-1 N−1。随后有M行数据,一条边的数据占用一行,每行包含三个数据,依次为该边连接的两个顶点编号和该边的权重。始终至少有一种方法能产生最小生成树。
输出格式
输出最小生成树的权重之和。
输入样例
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
输出样例
11
解题思路
测试代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
/** \brief 并查集初始化
*
* \param table size_t*
* \param number size_t
* \return void
*
*/
void init(size_t *table,size_t number){
for(size_t i=0;i<number;i++){///初始化时每个节点都是独立的集合
table[i] = i;
}
}
/** \brief 查找所在集合的根节点
*
* \param table size_t*
* \param id size_t
* \return size_t
*
*/
size_t find(size_t *table,size_t id){
size_t root,next,temp;
root = id;///寻找并查集根节点
next = table[root];
while(next!=root){
root = next;
next = table[root];
}
temp = id;///并查集路径压缩
next = table[temp];
while(next!=root){
table[temp] = root;///使路径上的各点直接指向根节点
temp = next;
next = table[temp];
}
return root;
}
/** \brief 将两点所在的集合合并
*
* \param table size_t*
* \param left size_t
* \param right size_t
* \return bool 合并前两点已在同一集合返回假,否则为真
*
*/
bool merge(size_t *table,size_t left,size_t right){
left = find(table,left);
right = find(table,right);
if(left!=right){
table[right] = left;
return true;
}
return false;
}
struct Edge{
size_t left;
size_t right;
int weight;
Edge(size_t left,size_t right,int weight):left(left),right(right),weight(weight){}
bool operator<(Edge &another){///重载运算符用于排序
return weight < another.weight;
}
};
int main(){
size_t n,m;
cin >> n >> m;
vector<Edge> edge;
for(size_t i=0;i<m;i++){///读入各边数据
size_t left,right;
int weight;
cin >> left >> right >> weight;
edge.push_back(Edge(left,right,weight));
}
sort(edge.begin(),edge.end());///将各边按权重升序排列
vector<size_t> table(n);
init(&table[0],n);
int sum = 0;
size_t num = 0;
for(vector<Edge>::iterator i=edge.begin();i!=edge.end();i++){
if(merge(&table[0],i->left,i->right)){///若边将两颗最小生成子树连接起来
sum += i->weight;
if(++num==n-1){///若生成树的边数达到点数减一,则最小生成树已经构建完成
break;
}
}
}
cout << sum << endl;
return 0;
}