描述
很久很久以前,森林里住着一群兔子。兔子们无聊的时候就喜欢研究星座。如图所示,天空中已经有了n颗星星,其中有些星星有边相连。兔子们希望删除掉一些边,然后使得保留下的边仍能是n颗星星连通。他们希望计算,保留的边的权值之和最小是多少?
9 A 2 B 12 I 25 B 3 C 10 H 40 I 8 C 2 D 18 G 55 D 1 E 44 E 2 F 60 G 38 F 0 G 1 H 35 H 1 I 35
216
题意很明确,是一道最小生成树问题,数据规模也不大,适合拿来练手。这里我采用的是Kruskal算法:用并查集来做,每次选取 未选取过的边中权值最小的,若这条边的两个端点不属于同一棵树,则合并它们所在的两棵树,直到所有节点合并为一棵树,即为所求。题目保证图是连通的,可以找到最小生成树,所以不需要考虑是否存在的问题,直接水过。
代码如下:
#include <iostream>
#include <algorithm>
using namespace std;
struct edge{ // 记录边的结构体
int s, e;
int w;
bool operator<( const struct edge& p )const{
return w < p.w;
}
}e[80];
int f[30]; // 保存父节点索引
int find( int i ){ // 路径压缩算法
if( f[i] == i ) return i;
return f[i] = find(f[i]);
}
int main(){
int n, k, w, cnt = 0, ans = 0;
char a, b;
scanf("%d",&n);
for( int i = 0; i < n; ++i ) // 初始化,每个节点的父亲初始情况下都是它自己
f[i] = i;
while( --n ){
cin.get();
scanf("%c %d",&a,&k);
while( k-- ){
cin.get();
scanf("%c %d",&b,&w);
e[cnt].s = a - 'A';
e[cnt].e = b - 'A';
e[cnt++].w = w;
}
}
// Kruskal算法求最小生成树
int tmp = n;
int fa, fb;
sort(e,e+cnt); // 按照权值由小到大排序
for( int i = 0; i < cnt; ++i ){
fa = find(e[i].s);
fb = find(e[i].e);
if( fa != fb ){ // 不属于同一并查集,合并,并查集个数减一
f[fb] = fa;
--tmp;
ans += e[i].w;
if( tmp == 1 ) // 合并到只剩一个并查集,退出
break;
}
}
cout<<ans; // 输出结果
return 0;
}