今天学习的是最小生成树。
最小生成树有两个关键点,一个是树,一个是最小。
树意味着连接点的数量是n,连接线的数量是n-1。
最小意味着位权之和最小。
而最小生成树有两种常见的算法。
一个是kruskal (克鲁斯卡尔算法)。
一个是prim (普里姆算法)。
kruskal算法的核心在于将边的大小按循序排列起来,再从小到大的去排列。如果遇到了一个圈,则跳过选到的最后一条边,去选择下一条边,直到选择了n-1条边。
//最小生成树
#include<iostream>
using namespace std;
struct edge
{
int u;//端点
int v;//端点
int w;//权位
};//排序,储存边的关系
struct edge e[10];
int n, m;
int f[7] = { 0 }, sum = 0;
void quicksort(int left, int right)
{
int i, j;
struct edge t;
if (left > right)
return;
i = left;
j = right;
while (i != j)
{
while (e[j].w >= e[left].w && i < j)
j--;
while (e[j].w <= e[left].w && i < j)
i++;
if (i < j)
{
t = e[i];
e[i] = e[j];
e[j] = t;
}
}
t = e[left];
e[left] = e[i];
e[i] = t;
quicksort(left, i - 1);
quicksort(i + 1, right);
return;
}
int getf(int v)
{
if (f[v] == v)
return v;
else
{
f[v] = getf(f[v]);
return f[v];
}
}
int merge(int v, int u)
{
int t1, t2;
t1 = getf(v);
t2 = getf(u);
if (t1 != t2)
{
f[t2] = t1;
return 1;
}
return 0;
}
int main()
{
int i;
int count = 0;
cin >> n >> m;
for (i = 1; i <= m; i++)
cin >> e[i].u >> e[i].v >> e[i].w;
quicksort(i, m);
for (i = 1; i <= n; i++)
f[i] = i;
for (i = 1; i <= m; i++)
{
if (merge(e[i].u, e[i].v))
{
count++;
sum = sum + e[i].w;
}
if (count == n - 1)
break;
}
cout << sum << endl;
getchar();
return 0;
}
以上是总代码。
void quicksort(int left, int right)
{
int i, j;
struct edge t;
if (left > right)
return;
i = left;
j = right;
while (i != j)
{
while (e[j].w >= e[left].w && i < j)
j--;
while (e[j].w <= e[left].w && i < j)
i++;
if (i < j)
{
t = e[i];
e[i] = e[j];
e[j] = t;
}
}
t = e[left];
e[left] = e[i];
e[i] = t;
quicksort(left, i - 1);
quicksort(i + 1, right);
return;
}
这是一个快速排序算法的实现,用于对边按照权重进行排序。排序后的边数组将按照权重从小到大的顺序排列。
int getf(int v)
{
if (f[v] == v)
return v;
else
{
f[v] = getf(f[v]);
return f[v];
}
}
这是在完成并查集的查找。
int merge(int v, int u)
{
int t1, t2;
t1 = getf(v);
t2 = getf(u);
if (t1 != t2)
{
f[t2] = t1;
return 1;
}
return 0;
}
这是在完成并查集。
int main()
{
int i;
int count = 0;
cin >> n >> m;
for (i = 1; i <= m; i++)
cin >> e[i].u >> e[i].v >> e[i].w;
quicksort(i, m);
for (i = 1; i <= n; i++)
f[i] = i;
for (i = 1; i <= m; i++)
{
if (merge(e[i].u, e[i].v))
{
count++;
sum = sum + e[i].w;
}
if (count == n - 1)
break;
}
cout << sum << endl;
getchar();
return 0;
}
这是主函数,首先读入顶点数量n
和边数量m
,然后读入每条边的信息并存储在e
数组中。接着,对e
数组进行快速排序,以便后续按照权重顺序选择边。然后,初始化并查集数组f
,并遍历排序后的边数组,依次尝试将每条边加入最小生成树中。如果加入该边不会形成环,则将该边加入最小生成树,并更新最小生成树的总权重和已加入的边数。当已加入的边数等于n-1
时,算法结束。最后,输出最小生成树的总权重。
以上便是克鲁斯卡尔算法。