1.定义
一张无向图,任意两顶点都是相互连通,并且是一个树结构,那么这棵树叫做生成树。当连接顶点之间的图有权值时,权值之和最小的树称为最小生成树。
2.算法
Kruskal算法
一种贪心算法,将图中的每个edge按照权值大小进行排序,每次从边集中取出权值最小并且两个顶点都不在同一个集合的边加入生成树中。
注:如果这两个顶点都在同一集合内,说明已经通过其他边相连,因此如果将这个边添加到生成树中,那么就会形成环。
【模板】最小生成树
|
|
#include<stdio.h>
#include<stdlib.h>
struct tree
{
int x,y,z;
}trees[200005];
int code[200005];
int n,m;
int count=0,add=0;
int fun(int num)
{
if(code[num]==num) return num;
return code[num]=fun(code[num]);
}
void sort()
{
struct tree s;
for(int i=1;i<=m;i++)
for(int j=i;j<=m;j++)
if(trees[i].z>trees[j].z)
{
s=trees[i];
trees[i]=trees[j];
trees[j]=s;
}
}
int cmp(const void *a, const void *b)
{//自定义比较函数
return ((struct tree*)a)->z-((struct tree*)b)->z;
}
int judge(int x, int y)
{//判断是否属于同一集合
int a = fun(x),b = fun(y);
if(a==b)
return 0;
code[b] = a;
return 1;
}
int main()
{
int i;
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++)
scanf("%d%d%d",&trees[i].x,&trees[i].y,&trees[i].z);
for(i=1;i<=n;i++)
code[i]=i;
qsort(trees,m,sizeof(struct tree),cmp);
for( i=1;i<=m;i++)
{ if(judge(trees[i].x,trees[i].y))//属于同一集合就不连接,否则就连接,并加权值
{
count++;
add+=trees[i].z;
}
if(count==n-1)
{
printf("%d\n",add);
break;
}
}
if(i>m)
printf("orz\n");
return 0;
}
Prime算法
对节点进行操作
1.建立一个数组用来存放权值,建立另一个数组用来存放结点,同时用于标记是否被访问过,建立边的最小堆。
2.开始遍历所有结点,如果没有被访问,则添加到结点数组,然后将其相连的边入堆。
3.从堆中取最小的边,然后判断目标节点是否被访问过,如果没有,将这个边加入生成树,并标记该节点被访问。
4.然后将目标节点所连接的边添加到最小堆中.
5.循环上面的操作,直到所有节点遍历完。
注意:
对单连通,从一个结点出发就可以直接找到完整的最小生成树,因此最外的for循环也可以为一个顺序结构,但多连通,就需要从不同结点出发。
/*prime算法生成最小生成树*/
void tree()
{
int min,i,j,k;
int ad[1000];
int low[1000];
low[0]=0; //初始化第一个权值为0,即v0加入生成树
ad[0]=0; //初始化第一个顶点下标为0
for( i=1;i<n;i++)
{
low[i]=e[0][i]; //将v0顶点已经与他有连接的结点的权值存入数组
ad[i]=0; //初始化都为v0的下标
}
for(i =1;i<n;i++)
{
min=65535; //初始化最小权值为65535
j=1;
k=0;
while(i<n)
{
if(low[j]!=0&&low[j]<min)
//如果权值不为0且权值小于min,则让当前权值成为最小值,将当前最小值的下标存入k
{
min=low[j];
k=j;
}
j++;
}
printf("(%d,%d)\n",ad[k],k); //输出当前顶点边中的权值最小的边
low[k]=0;
for(j=1;j<n;j++)
{ //如果下标为k的顶点的各边边权值小于此前这些顶点未被加入生成树的权值,将娇小的权值存入low相应的位置
if(low[j]!=0&&e[k][j]<low[j])
{
low[j]=e[k][j];
ad[j]=k;
}
}
}
}