浅谈最小生成树算法
最小生成树
最小生成树算法有2种
prim (点数^2)
kruskal (边^log边)
prim
其法:
- 把1放入集合中
- 找集合中的点的邻接点中边最小的(没有标记过的)
- 把此邻接点放入集合
- 标记
- 重复2,直到所有节点都被放进集合中
vector<int>d;
while(所有节点并没有都放d中)
{
for(i遍历d中所有已入的节点)
{
for(遍历i的邻接点)
{
if(i到j的权值是最小的&&边没有标记&&j没有标记)
{
保存i,j;
}
}
}
标记j;
标记边;
总和+=i到j的权值
}
具体地
while ((int)d.size() < n)//集合d
{
edge mx{-1, 114514};//.first 记录j .second 记录最大权值
int aa;//记录i
for (auto i : d)//遍历集合
for (auto j : a[i])//遍历i的邻接点
if (mx.second > j.second && !e[i][j.first] && !e[j.first][i]&& !b[j.first])
mx = j, aa = i;//找最小的,没有标记的
d.push_back(mx.first);//放入集合
b[aa] = b[mx.first] = 1;//标记
e[aa][mx.first] = 1;//标记
s += mx.second;//总和+
}
kruskal
其法:
建立并查集fa数组记录个点的父亲,初始化为自己(fa[2]=2)
一个找父亲的函数ff()
for(遍历所有边)
{
1. 看边数有没有等于n-1(树的性质),则break
2. 找没被选过的最小边
1. 看他们是否有被链接过(ff(from)==ff(to)),没有则让to的父亲重置为from
2. 边数加一
3. 总和加上全值
}
因为找最小的,可以考虑用堆或sort优化,
推荐用sort,,trust me!
cpp code如下
int n;//总点数
int m;//总边数
struct ee
{
int f, t;//from and to
int v;//权值
ee() {}
ee(int a, int b, int c) : f(a), t(b), v(c) {}//构造函数
friend bool operator<(ee a, ee b) { return a.v < b.v; }//重载以排序
} b[N];
int ff(int x)//用递归找父亲
{
if (fa[x] != x)
fa[x] = ff(fa[x]);
return fa[x];
}
sort(b + 1, b + 1 + m);//按权排序边
for (int i = 1; i <= m; i++)//遍历边
{
if (bs == (n - 1))//边数==点数-1
{
printf("%d\n", sum);
break;
}
f = ff(b[i].f), ft = ff(b[i].t);
if (f != ft)//判断父亲是否相同
fa[ft] = f, sum += b[i].v, bs++;//重置父亲,总和+,边数+1
}
}