基本概念
在无向图中,连通且不含环的图称为树。
- 生成树的定义
在一个v个点的无向连通图中,取其中v-1条边,并连接所有的顶点,所得到的子图称为原图的一棵生成树; - 最小生成树
在一个带权的无向连通图中,各边权和最小的一棵生成树即为原图的最小生成树; - 最小边原则
图中权值最小的边(如果唯一的话)一定在最小生成树上; - 唯一性原则
对于一个图G,如果图中的边权值都不相同,则图的最小生成树是惟一的,反之不然;
prim算法
思想
prim算法是一种贪心算法她最初将无向联通图G中所有顶点V分成两个顶点集合VA和VB。在计算过程中VA中的点为已经选好的连接入生成树的点,否则属于VB。最开始的时候VA只包含任意选取的图G中的一个点u,其余的点属于VB,每次添加一个VB中的点(集合VB到集合VA中距离最小的那个点)到集合VA。直到V个顶点全部属于VA,算法结束。显然出发点不同,最小生成树的形态就不同,但边权和的最小值是唯一的。
算法步骤
选定图中的任意一个顶点V0,从V0开始生成最小数:
- 初始化dis[V0]=0,其他点的距离值dis[i]为极大值。其中dis[i]表示集合VB中的点到集合VA中的点的距离值。
- 经过N次以下步骤,最后得到最小生成树:
①. 选择一个未被标记的点k,并且dis[k]的值是最小的;
②.标记点k进入集合VA;
③.以k为中心点,修改未标记点j,即VB中的点到VA的距离值; - 得到最小生成树。
伪代码
for(int i=1;i<=n;i++) dis[i]=inf;
dis[1]=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
找dis[j]最小&&vis[j]==0的点j,
vis[j]=1;
更新j->k的dis[k];
时间复杂度
最小边、权的数据结构 | 时间复杂度(总计) |
---|---|
邻接矩阵、搜索 | O(V^2) |
二叉堆、邻接表 | O((V + E) log(V)) = O(E log(V)) |
斐波那契堆、邻接表 | O(E + V log(V)) |
通过邻接矩阵图表示的简易实现中,找到所有最小权边共需O(V)的运行时间。使用简单的二叉堆与邻接表来表示的话,普里姆算法的运行时间则可缩减为O(ElogV),其中E为连通图的边数,V为顶点数。如果使用较为复杂的斐波那契堆,则可将运行时间进一步缩短为O(E+VlogV),这在连通图足够密集时(当E满足Ω(VlogV)条件时),可较显著地提高运行速度。
图例描述
图例 | 说明 | 不可选 | 可选 | 已选(VA) |
---|---|---|---|---|
![]() |
此为原始的加权连通图。每条边一侧的数字代表其权值。 | - | - | - |
![]() |
顶点D被任意选为起始点。顶点A、B、E和F通过单条边与D相连。A是距离D最近的顶点,因此将A及对应边AD以高亮表示。 | C, G | A, B, E, F | D |
![]() |
下一个顶点为距离D或A最近的顶点。B距D为9,距A为7,E为15,F为6。因此,F距D或A最近,因此将顶点F与相应边DF以高亮表示。 | C, G | B, E, F | A, D |
![]() |
算法继续重复上面的步骤。距离A为7的顶点B被高亮表示。 | C | B, E, G | A, D, F |
![]() |
在当前情况下,可以在C、E与G间进行选择。C距B为8,E距B为7,G距F为11。点E最近,因此将顶点E与相应边BE高亮表示。 | 无 | C, E, G | A, D, F, B |
![]() |
这里,可供选择的顶点只有C和G。C距E为5,G距E为9,故选取C,并与边EC一同高亮表示。 | 无 | C, G | A, D, F, B, E |
![]() |
顶点G是唯一剩下的顶点,它距F为11,距E为9,E最近,故高亮表示G及相应边EG。 | 无 | G | A, D, F, B, E, C |
![]() |
现在,所有顶点均已被选取,图中绿色部分即为连通图的最小生成树。在此例中,最小生成树的权值之和为39。 | 无 | 无 | A, D, F, B, E, C, G |
模板
#include<iostream>
using namespace std;
const int maxn=1<<31-1;
int map[505][505],vis[505],dis[505],n,m,ans;
//vis[i]标记顶点i是否在最小生成树中
//dis[i]表示点i与当前生成树中的点有连边的边长最小值
void read()//建图 <