一、概念解释

首先解释几个概念:
  • 连通图:在无向图中,若任意两个顶点vi与vj都有路径相通,则称该无向图为连通图。

  • 强连通图:在有向图中,若任意两个顶点vi与vj都有路径相通,则称该有向图为强连通图。

  • 连通网:在连通图中,若图的边具有一定的意义,每一条边都对应着一个数,称为权;权代表着连接连个顶点的代价,称这种连通图叫做连通网。

  • 生成树:一个连通图的生成树是指一个连通子图,它含有图中全部n个顶点,但只有足以构成一棵树的n-1条边。一颗有n个顶点的生成树有且仅有n-1条边,如果生成树中再添加一条边,则必定成环。

  • 最小生成树:在连通网的所有生成树中,所有边的代价和最小的生成树,称为最小生成树。

(转自勿在浮沙筑高台)

  简单来说,最小生成树就是在连通图中找出一颗树,这棵树满足这样的要求:1、可以将图中的所有顶点都连通;2、这颗树的边的权重和是最小的(具体到实际问题上,边的权重可能是成本、路程等)

  求最小生成树一般有两种算法,一种是Prim算法,另一种则是Kruskal算法。

二、Prim算法

  Prim使用的是贪心的思想,与Dijkstra有异曲同工之处。它的原理是,先从图中任意选择一个起点,并记录下与这个点直接相连的点对应的边的权值,然后找到这些边中权值最小的,并将这个权值加到最终结果中,同时在vis数组里将选定的起点标记。然后然后我们将“起点”转移到刚才选择的权值最小边所对应的另一个顶点,重复上述过程,再找出一条与新起点直接相连的权值最小边,把它的权值加到最终结果中......不断重复以上过程共n-1次,即可找出最小生成树。代码如下:

  Prim与Dijkstra的相同之处在于,两者都用到了贪心思想,且都有点与点之间的转移。但不同之处在于,Dijkstra是求从一个点到另一个点的最短路,而Prim是求最小权值的树。这就决定了两者之间的一个区别,即Dijkstra的dis[]数组用于存放起点到每一个点的最短距离,而Prim的dis[]数组用于存放当前点到与它直接相连的点的最小权值。(我怎么觉得我在说废话)

  再来一道Prim的模板题→HDU1233 还是畅通工程

  这题只需要直接套用模板即可,没什么需要注意的地方。可以通过这道题感受一下Prim怎么用。

代码如下:

三、Kruskal算法

  与Prim的基于顶点不同,Kruskal是一种基于边的算法。它的原理是,首先将一个连通图的所有边存起来,然后根据权值从小到大对这些边进行排序。然后每次按权值从小到大的顺序取出一条边,检查这条边的两个顶点是否在同一个集合内,若是,则这条边弃之不用且永远不会被使用(否则会产生闭环);否则,选用这条边,作为最小生成树的其中一条边(即将两个这条边对应的两个顶点合并入一个集合中)。而在检查与合并的过程中,为了保证效率,我们可以使用并查集来实现。代码如下:

来一道Kruskal的模板题→HDU1875畅通工程再续

代码如下:

两种算法的图解,可参考此文章→图的基本算法(最小生成树