什么是最小生成树??当在一个图中,这个图描绘了节点与节点的关系,表现为这个节点到下一个节点的 “代价” 那么我们想把这个图中的各个节点用最小的代价连接起来该怎么办呢??这里就要知道一个关键词,最小生成树。 那么是否有实际应用呢?? 例如:要在n个城市之间铺设光缆,主要目标是要使这 n 个城市的任意两个之间都可以通信,但铺设光缆的费用很高,且各个城市之间铺设光缆的费用不同,因此另一个目标是要使铺设光缆的总费用最低。这就需要找到带权的最小生成树。 好下面我们就直接贴代码:在代码中有注释能直接更快的明白最小生成树该怎么写! 代码一:prim(普里姆)算法
/*
prim算法是一个不高效,并且占用内存较为高的一种最小生成树算法。但是好理解。这样呢也确实一种方法。
毕竟学习算法就是学习思想嘛!
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,mann[100][100],vis[100],dis[100];
//mann中存储节点之间的距离,默认是最大值mx
//vis数组是标记节点是否已经被加入的了最小生成树里边
//dis是记录最小生成树到各个未加入最小生成树节点的距离
const int mx=1<<29;//上限,这个程序的最大值
int pri(){
for(int j=1;j<=n;j++){
dis[j]=mann[1][j];//初始化dis,以后会更新
}
memset(vis,0,sizeof(vis));//初始化vis,表示都未加入最小生成树
vis[1]=1;//表示节点1加入最小生成树。
int minn,to,sum=0;
for(int i=1;i<n;i++){
minn=mx;
for(int j=1;j<=n;j++){
if(!vis[j]&&dis[j]<minn){//是否加入最小生成树,并且是最小值
minn=dis[j];
to=j;//记录最小生成树到其他节点的代价最小的下标
}
}
sum+=minn;//总代价+当前算出的小代价
vis[to]=1;//表示当前找到最小节点加入最小生成树
for(0int j=1;j<=n;j++){
if(!vis[j]){//判断是否加入最小生成树
dis[j]=min(dis[j],mann[j][to]);//更新各个单独节点到最小生成树的最小距离
}
}
}
return sum;
}
int main(){
while(~scanf("%d%d",&n,&m)){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){//初始化某节点到某节点为当前程序的极大值
mann[i][j]=mx;
}
}
int f,t,v;//表示f节点到t节点的代价为v
for(int i=1;i<=m;i++){
scanf("%d%d%d",&f,&t,&v);
mann[f][t]=v;
mann[t][f]=v;
}
printf("%d\n",pri());
}
return 0;
}
介绍完prim(普里姆)算法就该介绍 kruskal(克鲁斯卡尔)算法了 ,kruskal算法涉及到了并查集的知识但是 如果并查集学得好 看一眼就会知道原理了。
代码二:kruskal(克鲁斯卡尔)算法
/*
这个算法比起prim算法是比较节省空间的。并且感觉时间上也是很节省的。因为是先排序再查找。
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,record[100];
//n,m n代表节点的总数,m代表在节点间共有多少种代价。
//record代表各个节点到 “祖” 的距离,学过并查集都知道这个东西
struct node{
int f,t,v;//f到t节点的代价为v
}Array[100];
int cmp(node a,node b){
return a.v<b.v;
}
int _find(int x){
if(x!=record[x]){
record[x]=_find(record[x]);//查找到 祖 值后进行路径压缩
}
return record[x];
}
int solve(){
int sum=0,cnt=0;
for(int i=1;i<=m;i++){
int f=_find(Array[i].f);
int t=_find(Array[i].t);
if(f!=t){//在最小生成树里边,都有着共同的 “祖” 值。没有共同的祖值表示 未加入到最小生成树里边
record[f]=t;
sum+=Array[i].v;//总代价增加
cnt++;
}
if(cnt==n-1){//因为如果把所有节点连上了后肯定需要n-1个边
return sum;
}
}
return -1;
}
int main(){
while(~scanf("%d%d",&n,&m)){
for(int i=1;i<=n;i++){
record[i]=i;//初始化各个节点的 "祖" 值
}
for(int i=1;i<=m;i++){
scanf("%d%d%d",&Array[i].f,&Array[i].t,&Array[i].v);
}
sort(Array+1,Array+m+1,cmp);//对各个节点 代价 进行排序。按照从小到大
printf("\n%d\n",solve());
}
return 0;
}
最后: Prim算法和Kruskal算法都能从连通图找出最小生成树。区别在于Prim算法是挨个找,而Kruskal是先排序再找。是不是感觉最小生成树很简单呢???当然如果你想要把 Prim算法和Kruskal算法合并的话可以尝试用链表这个数据结构存储节点–>排序–>像Kruskal算法那样生成最小生成树!!
心灵鸡汤:
既然选择程序,就要匠心所致;
既然选择远方,就要风雨兼程