学习算法的最好方式,我认为是应从思考一些有趣的题目开始。学习一个算法之前要知道这个算法的价值是什么。
题目大意:询问你在各个村庄之间修公路,最少的花费是多少。
我们可以把“村庄"抽象成一个一个点,把”修公路的费用"抽象成连接两点的边值(权重)。
我们要求解决的是求出 把这些点(村庄)都连起来(修公路)时的所构成的树的权重总和最少(最少费用)是多少,即最小生成树的权重和。
如图:
注:生成树不含环路,且在有相同权重得情况下最小生成树可能不止一颗,但权重和一定相同。
则如何构建最小生成树成为重中之重!
下面介绍两种算法:Prim算法 和 Kruskal算法。
算法原理先介绍,证明会放在最后,因为这两种算法简单且看上去就像是那么回事~
Prim算法:
Prim算法的思想是:取图中任意一点作为起点放入树中,并向邻近树的点不断延伸,每次延伸的点要求满足到树的距离最短。
我们采用dis[]记录点离树的距离,并采用val[]标记在树中的点,两数组实时更新。
如图:
1:
2:
3:
4:
5:
这里省略一步连接点四的图,所以布置共6步,对应的图中6个点。
所以你知道所构建的最小生成树的权重和是多少吗?没错就是把每幅图中dis[]中红色数字加起来即可,即对应上面的图MinDis=2+4+5+3+7=21。其实dis[]不用在每步更新在树中的点,而是保留下来,这样最后遍历加一遍dis[]也可以得到答案(前提是所以点都可以连成一棵树)。
很可惜C++中没有