这几天,离散数学学习了图论。自然也引起了一些我对图和树的一些有益的思考。今天,就通过一道hdu的acm题来简述最小生成树的两种基本算法---kruskal算法和prim算法。
下面是题目,在hdu的oj中编号为1233显然,这是一种求最小生成树的问题。所谓树就是由枝丫和结点而构成的数据结构。而prim和kruskal算法分别从结点和枝丫来考虑了这个问题。prim(普里姆算法)这是一种由点来构成最小生成树的算法。</p><p>首先,构建一个只有孤立点而无枝丫的空图。而后,选择一个起点a1,将其加入已知域当中,并找出距离a1最近的点标为a2,将其加入已知域当中,并更新距离已知域的距离的集合,再递归寻找距离这个已知域最近的点。循环往复,直到所有的点都被纳入已知域当中。这是第一种贪心的思路。kruskal算法这个算法就是从边来进行最小生成树生成的。设此时有n个点,首先,将路径的长短排序,选择最短的那一边,而后,选择第二条边。但此时,对这条边的端点便有了要求。由于我们要找的是最小生成树,所以每一个点只需联通1次即可,故这棵树只有n-1条边,且两两两点必然可以联通。故我们不需要联通具有同一个根的两个点(即两点是由前面的点分叉而得)。所以,我们设置了两个数组来衡量一个点的权重和母根(即下面的father和son数组),逐次递归,当生成树达到n-1条边的时候,最小生成树完成。
普里姆算法:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define infinity 0x3f3f3f3f
int road[110][110],lowcase[110];
int num;
bool map[110];
int prime()
{
int i,j,k,temp,sum=0;
memset(map,false,sizeof(map));
map[1]=true;
for(i=1;i<=num;i++)
lowcase[i]=road[1][i];
for(i=1;i<=num;i++)
{
temp=infinity;
for(j=1;j<=num;j++)
{
if(!map[j]&&temp>lowcase[j])
{
temp=lowcase[j];
k=j;
}
}
if(temp == infinity) break;
map[k]=true;
sum+=temp;
for(j=1;j<=num;j++)
{
if(!map[j]&&lowcase[j]>road[k][j])
lowcase[j]=road[k][j];
}
}
return sum;
}
int main()
{
int i,a,b,c,tem,result;
while(scanf("%d",&num)!=EOF&&num)
{
memset(road,infinity,sizeof(road));
tem=(num*(num-1))/2;
for(i=1;i<=tem;i++)
{
scanf("%d %d %d",&a,&b,&c);
road[a][b]=road[b][a]=c;
}
result=prime();
printf("%d\n",result);
}
return 0;
}
kruskal算法:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
struct road
{
int len;
int a,b;
};
road map[5050];
int comp (road a,road b)
{
return a.len < b.len;
}
int father[110],son[110],num,tem;
int rootsearch(int x)
{
return x == father[x] ? x : rootsearch(father[x]);
}
int kruskal()
{
int i,total=0,sum=0;
int root1,root2;
for(i=1;i<=num;i++)
{
son[i]=1;
father[i]=i;
}
for(i=0;i<tem;i++)
{
root1=rootsearch(map[i].a);
root2=rootsearch(map[i].b);
if(root1==root2)continue;
else if(root1>=root2)
{
father[root2]=root1;
son[root1]+=son[root2];
}
else
{
father[root1]=root2;
son[root2]+=son[root1];
}
sum+=map[i].len;
total++;
if(total==num-1)break;
}
return sum;
}
int main()
{
int result,i;
while(scanf("%d",&num)!=EOF&&num!=0)
{
tem=(num*(num-1))/2;
for(i=0;i<tem;i++)
scanf("%d %d %d",&map[i].a,&map[i].b,&map[i].len);
sort(map,map+tem,comp);
result=kruskal();
printf("%d\n",result);
}
return 0;
}