最小生成树,是指在面对多个顶点以及多条路径中,寻找可以遍历所有顶点所需要的最小距离。
常见的算法可以分为两类prime和kurskal两种 ,虽然是求同一类问题,但两个算法的思路确实截然不同。prime算法与最短路径算法djikstra在方法上很类似,而kurskal算法是利用并查集的方法通过排序,通过简单的贪心算法将每一个顶点的可用的最短边依次加入,其结果即为最小生成树。
例题一(kurskal)
POJ 1287
前面说到,李云龙集结了所有驻扎在外的部队,想要进攻平安县城,但是平安县城太大了,李云龙的部队太多了,把平安县城围起来之后自己传达命令很不方便,而且由于山本的手下有个狙击手专打通讯员,派遣通讯员很有可能发生意外使得命令传达不到,所以李云龙决定派工程兵修建电话线。由于这是一个危险的任务,而且为了尽快建好通讯电话网络,工程兵需要在最短的时间内建立一个能够传达命令的电话网络。
多样例输入,且至少有一个可行方案。
每个样例的第一行有两个整数,P(需要建立通讯的部队数),R(部队与部队之间能够建立通讯电话的总数)。
接下来的R行输入路径,每行含3个整数,前两个数表示部队编号,最后一个数表示修建这条电话线所需要的时间
当P为0时输入结束。
P最大为50,每条电话线修建时间最多为100。
i和j之间的路径可以表示为 i j 或 j i
对于每个样例,输出一个数表示建立完善的通讯电话网络所需要的最短时间
输入
1 0
2 3
1 2 37
2 1 17
1 2 68
3 7
1 2 19
2 3 11
3 1 7
1 3 5
2 3 89
3 1 91
1 2 32
5 7
1 2 5
2 3 7
2 4 8
4 5 11
3 5 10
1 5 6
4 2 12
0
输出
0
17
16
26
思路:
一眼就可以看出本题求的是最小生成树,直接使用kurstal算法即可求得结果直接上代码
代码:
#include<stdio.h>
#include<algorithm>
using namespace std;
#include<string.h>
#include<stdlib.h>
int m,n,sum,ans;
int i,j,p,r;
int vis[1100];
struct edge
{
int u,v,w;
}ed[5000];
void numb()
{
for(i=1;i<=p;i++)
{
vis[i]=i;
}
}
int find(int x)
{
int p=x;
while(x!=vis[x])
x=vis[x];
while(vis[p]!=x)
{
int j=vis[p];
vis[p]=x;
p=j;
}
return x;
}
void join(int x,int y)
{
int fx=find(x);
int fy=find(y);
if(fx!=fy)
{
vis[fx]=fy;
ans++;
sum+=ed[i].w;
}
}
void kru()
{
numb();
ans=0;sum=0;
for(i=0;i<r;i++)
{
int u=ed[i].u;
int v=ed[i].v;
join(u,v);
if(ans==r-1)
break;
}
}
bool cmp(edge a,edge b)
{
return a.w<b.w;
}
int main()
{
while(~scanf("%d",&p),p)
{
scanf("%d",&r);
for(i=0;i<r;i++)
scanf("%d%d%d",&ed[i].u,&ed[i].v,&ed[i].w);
sort(ed,ed+r,cmp);
kru();
printf("%d\n",sum);
}
return 0;
}
例题二(prime)
POJ 2421
题目
给定一个 nn 个点的完全图,求该图的最小生成树,其中有一些边必选。
其中最小生成树代表图中边权和最小的树。
输入
第一行一个整数 n(3≤n≤100)。
接下来 nn 行,每行 n 个数,其中第 i 行第 j 列的数代表 i 到 j 这条边的长度,长度不大于 1000。
接下来 q 行,每行两个数 i,j,表示 i 到 j 的这条路必选。
输出
一行一个正整数,代表最小生成树的权值和。
思路:
题目本身还是一个简单的最小生成数的运用,但需要注意一点是每一个被标记的边要将对应的值加到总长度同时将这条边长度写为零,其余正常按最小生成树算法步骤进行即可。
代码:
#include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std;
int mp[105][105];
int book[105];
int dis[105];
int sum;
int n;
void prime()
{
int count,v,minn;
book[1]=1;
for(int i=1;i<=n;i++)
dis[i]=mp[1][i];
count=1;
while(count<n)
{
minn=99999999;
for(int i=1;i<=n;i++)
{
if(book[i]==0&&dis[i]<minn)
{
v=i;
minn=dis[i];
}
}
book[v]=1;
count++;
sum+=dis[v];
for(int k=1;k<=n;k++)
{
if(book[k]==0&&dis[k]>mp[v][k])
{
dis[k]=mp[v][k];
}
}
}
printf("%d",sum);
}
int main()
{
int sum,m,x,y;
sum=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
scanf("%d",&mp[i][j]);
}
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
scanf("%d %d",&x,&y);
sum+=mp[x][y];
mp[x][y]=mp[y][x]=0;
}
prime();
}
总结:最小生成树的两个算法可以说不相上下,作者本人也很菜,很难区分两个算法具体的使用场合,简单来说,就时间复杂度而言,在稠密图中prime的运算速度相较于kurstal会快,相同的,稀疏图中kurstal更占优势。