生成最小的生成树的过程其实就是寻找安全边的过程,prim和kruskal从图的两个基本构成出发,即边和点的两个不同的角度,来寻找安全边。
安全边:
假设集合a是最小生成树的一个子集,我们要做的就是每次寻找一条边(u,v)。将其加入到集合a中,使得集合a与边(u,v)的并集仍然是最小生成树的子集,则称这条边是安全边。
定理:
首先设图g=(v,e)是一个无向联通图,设集合a为e的一个最小生成树,设(s,v-s)是尊重集合a的一个切割,又设边(u,v)是横跨切割的轻量级边,则边(u,v)对于集合a来说是安全的。
1.prim(点)
算法从任意的点开始,每次在集合a和a之外的节点中的所有边汇总选择一条轻量级的边,加入到a中,有定理可知,边a总是安全的。故当算法结束的时候,最小生成树是正确的,且本策略属于贪心的方法,但是是正确的。
简单的prim算法就是基本的循环就能跑出来结果,下附上简单的代码:
- while(true)
- {
- if(m==n) //生成树完成后跳出循环
- break;
- min_w=inf; //每次寻找续最小的边,记得是在循环内部每次进行初始化
- for(int i=2;i<=n;i++)
- {
- if(!u[i] && low_dis[i]>dist[s][i])
- low_dis[i] = dist[s][i]; //更新树到每个点的距离
- if(!u[i] && min_w>low_dis[i])
- {
- min_w = low_dis[i]; //寻找最小的距离
- point=i;
- }
- }
- s=point; //每次更新源点
- u[s]=true; //吧选过的点标记
- m++;//次数加一
2.kruskal(边)
在所有连接不同树的边里面,每次选取一条最小的边(u,v),由于边一定是链接集合a与其他树的轻量级边,利用贪心的做法,其实和prim算法一样,利用了定理:
附上简单的代码:
使用了较为渐大的数据结构:并茶集:
struct node
{
int x;int y;
int wei;
}edge[maxn*maxn/2];
bool cmp(node a,node b)
{
return a.wei<b.wei;
}
int findf(int x)
{
if(x!=father[x])
father[x]=findf(father[x]);
return father[x];
}
void ini(int x)
{
father[x]=x;
}
void un(int x,int y,int w)
{
if(x==y)
return ;
father[y]=x;
if(w>cun)
cun=w;
}
void solve(int len)
{
for(int i=1;i<=len;i++)
un(findf(edge[i].x),findf(edge[i].y),edge[i].wei);
}