一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。 [1] 最小生成树可以用kruskal(克鲁斯卡尔)算法或prim(普里姆)算法求出。
Prim在稠密图中比Kruskal优,在稀疏图中比Kruskal劣。
Kruskal:
#include <iostream>
#include <cstring>
#include <algorithm>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn = 1e3 + 5;
struct edge
{
int u;
int v;
int w;
}e[maxn]; //存图
bool cmp (struct edge a, struct edge b) //排序
{
return a.w < b.w;
}
int f[maxn];
void init (int n) //初始化
{
for (int i = 1; i <= n; i++){
f[i] = i;
}
}
int getf (int x) //找根
{
if (f[x] == x) return x;
return f[x] = getf(f[x]);
}
int merge(int x, int y) //合并,核心代码
{
int t1 = getf(x);
int t2 = getf(y);
if (t1 != t2){
f[t2] = t1;
return 1;
}
return 0;
}
int main()
{
int n, m; //点,边
cin >> n >> m;
memset(f, 0, sizeof(f));
memset(e, 0, sizeof(e));
init(n);
for (int i = 1; i <= m; i++){
cin >> e[i].u >> e[i].v >> e[i].w; //存图
}
sort(e + 1, e + n + 1, cmp); //从1开始排序,都+1
int sum = 0;
for (int i = 1; i <= n; i++){
if (merge(e[i].u, e[i].v)){ //不连通则使用这条边
sum += e[i].w;
}
}
cout << sum << endl;
return 0;
}
Prim:
#include <iostream>
#include <cstring>
#include <algorithm>
#define inf 0x3f3f3f3f
using namespace std;
const int MAX=1e3+5;
int map[MAX][MAX],dis[MAX],book[MAX];
int main()
{
int n,m; //点,边
cin>>n>>m;
memset(book,0,sizeof(book));
memset(map,0,sizeof(map));
for(int i=1;i<=n;i++) //初始化
{
for(int j=1;j<=n;j++)
{
if(i==j) map[i][j]=0;
else map[i][j]=inf;
}
}
int a,b,c;
for(int i=1;i<=m;i++) //存图
{
cin>>a>>b>>c;
if(map[a][b]>c)
{
map[a][b]=map[b][a]=c;
}
}
for(int i=1;i<=n;i++) //距离初始
{
dis[i]=map[1][i];
}
int count=0,sum=0,Min,u;
book[1]=1;
count++;
while(count<n) //核心代码
{
Min=inf;
for(int i=1;i<=n;i++)
{
if(book[i]==0&&dis[i]<Min)
{
Min=dis[i];
u=i;
}
}
book[u]=1;
count++;
sum+=dis[u];
for(int v=1;v<=n;v++)
{
if(book[v]==0&&dis[v]>map[u][v])
{
dis[v]=map[u][v];
}
}
}
cout<<sum<<endl;
return 0;
}