P r i m Prim Prim的过程
此处以最小生成树为例。最大生成树,其实也是可以使用 p r i m prim prim 的。 P r i m Prim Prim 的过程是:
首先,任意选择一个点,作为一个连通块。接下来,重复执行下面的步骤,直到所有点都被加入了连通块中:
- 定义两个点之间的距离,为两个点之间的最短路。
- 定义一个点 x x x 到当前连通块 S S S 的距离,为 min v ∈ S d i s ( x , v ) \min_{v\in S}dis(x,v) minv∈Sdis(x,v) 。即 x x x 到达 S S S 中的某个点的距离,取最小值。
- 找到一个点,这个点到当前连通块的距离最小。
- 将这个点加入连通块,并且加入它的距离所对应的边。
有一个特点,就是新加入的点一定与当前联通块中的某个点直接相邻。
正确性证明
利用一种反证法。我们假设某一步搞错了,看看接下来会发生什么。
这里的“出错”,我们定义成:加入了一条错误的边(它本不属于最小生成树,但程序误认为其应当属于)。
最初:绝对正确
无论怎么说,只选择了一个点的时候是一定正确的——此时一条边也没有,怎么会有搞错的边呢?
中途:万一错了
假设某一次加边,加错了!在此之前,我们所加入的边都是正确的。
不妨设原本正确的连通块是
T
0
T_0
T0 ,此时加入了一条出错的边
(
u
,
v
)
(u,v)
(u,v) ,那么整个程序结束后应该长这样:
生成树包含每一个点,所以
T
0
+
T
1
=
V
T_0+T_1=V
T0+T1=V 。此处的
V
V
V 指的是所有的点。
正确的生成树是什么呢?应该是
T
0
,
T
1
T_0,T_1
T0,T1 仍然是那两个
T
0
,
T
1
T_0,T_1
T0,T1 ,没有发生变化。
根据prim的过程
,
T
0
T_0
T0 周围最短的一条边(距离最小的)是
(
u
,
v
)
(u,v)
(u,v) ,所以
(
x
,
y
)
(x,y)
(x,y) 的权值是大于
(
u
,
v
)
(u,v)
(u,v) 的。那么,我们不妨在正确的生成树
身上做一点调整:我们把
(
x
,
y
)
(x,y)
(x,y) 删掉,换上
(
u
,
v
)
(u,v)
(u,v) 。
现在,我们得到的是一个生成树吗?我想,是的。
考虑生成树的定义:恰好 n − 1 n-1 n−1 条边,并且覆盖了所有的点,连通。
- 连通: T 0 T_0 T0 内部本来是连通的, T 1 T_1 T1 也是。我用一座桥 ( u , v ) (u,v) (u,v) 把二者连接了起来,当然仍然是连通的。
- 恰好 n − 1 n-1 n−1 条边:当然是这样的。
- 覆盖了所有的点:当然是这样的。
于是,我们得到了新的生成树。可是,新的生成树的权值更小!——我们删掉了较大的权值,又加入了较小的权值。
我们找到了比最小生成树更小的生成树?相当于是 x < 0 x<0 x<0 的非负实数解。不存在的!
所以 p r i m prim prim 是没有错误的。没有错误,意味着是正确的。
后话
我曾经不相信 p r i m prim prim 是正确的,想通后写了这篇博客。
但当时自己写的并不好(而且这是我的第一篇博客 -_-#)。
现在重新修改了两三下——因为这篇博客的阅读量最多。我也不知道为什么,我明明感觉这篇博客是没有什么技术含量的啊?
我为这篇博客的未修改版道歉!(因为我自己都看不下去)