最小生成树小结
问题引入:
新兴城镇之间需要修建道路,两两城镇之间修建道路所需的花费是不一样的,在保证所有城镇相通的情况下,求最小花费。
这道题需要用到最小生成树的相关知识。
一、最小生成树的概念
1. 最小生成树的定义
在一个\(|V|\)个点的无向连通图中,取\(|V|-1\)条边,并使所有点相连,所得到的子图被称为原图的一棵生成树;在一个带权的无向连通图中各边权之和最小的一棵生成树即为原图的最小生成树。
2. 最小生成树的性质
-
图中权值最小的边(如果唯一的话)一定在最小生成树上。
-
对于一个图\(G\),如果图中的边权值都不相同,则图的最小生成树是唯一的,反之亦然。
-
一个图\(G\),各个最小生成树的形态不同,但权值相同的边的数量一定相等。
3. 关于对性质3的证明
在一个图\(G\)中,\(A\),\(B\)是它的两棵不同的最小生成树。组成\(A\)的边为\(\{a_i\}\),组成\(B\)的边为\(\{b_i\}\),现在我们需要证明,把\(A\),\(B\)边的权值按从小到大排序后的序列是一样的,即对于所有\(i\),都有\(w(a_i)=w(b_i)\)。
思考在第\(i\)个位置,第一次出现了\(a_i\ne b_i\)。
不妨设\(w(b_i)\le w(a_i)\)。
情况\(1\):\(b_i\)在\(A\)中,那么必存在\(a_j=b_i\),并且\(j>i\)(因为\(i\)是第一次出现不相同的边啊)。这时即有\(w(b_i)\le w(a_i)\le w(a_j)=w(b_i)\),所以\(w(b_i)=w(a_j)=w(a_i)\),所以交换\(a_i\)和\(a_j\)的位置并不会影响\(A\)的有序序列,两棵树在第\(i\)号位置变成了一条边。
情况\(2\):\(b_i\)不在\(A\)中,考虑把它加入\(A\)中,则形成了一个环,并且环上所有权值\(v\le w(b_i)\)(不然\(A\)就不是最小生成树)。也就是说,这个环上存在着另一条边\(a_j\)不在\(B\)中(如果在\(B\)中就会形成环),且\(j>i\)(\(i\)是第一次出现不相同的边),\(w(a_j)\le w(b_i)\)。于是,\(w(b_i)\le w(a_i)\le w(a_j)\le w(b_i)\),同样三者相等。那么我们就可以交换\(b_i\)与\(a_j\),没有影响到\(A\)的有序序列,然后转换成情况\(1\)。
二、两种计算最小生成树的算法
1. Prim算法
贪心算法,原理略。
inline void Prim() {
memset(vis, 0, sizeof vis);
int s = n;
while(s --) {
minn = INF;
for (int i = 1;i <= n; ++i) {
if (!vis[i] && v[i] < minn) {
minn = v[i], pos = i;
}
}
vis[pos] = 1, ans += v[pos];
for (int i = 1;i <= n; ++i) {
if (!vis[i] && v[i] > dis[pos][i]) {
v[i] = dis[pos][i];
}
}
}
}
2. Kruskal算法
同样的贪心算法,同样地略。
inline void Kruskal() {
sort_by_w(edg + 1, edg + 1 + m, cmp);//从小到大
for (int i = 1;i <= m; ++i) {
if(cnt >= n) return;
int x = edg[i].u, y = edg[i].v;
int fx = find(x), fy = find(y);
if (fx != fy) {
cnt ++;
ans += edg[i].w;
f[fx] = fy;
}
}
}
三、练手题目&一句话分析
1. 黑暗城堡
求最短路树计数,那先\(SPFA\)跑一遍\(dis\),然后枚举每个点,乘法原理,就行了。
2. 北极通讯网络
把题意转换下,就是叫你求第\(k\)大最小生成树上的边,\(OK\)。
3. 构造完全图
思考\(Kruskal\)算法的流程,每次加边时,对答案的贡献应是\((size[x]\times size[y] - 1) \times (w + 1)\)。
4. 秘密的牛奶运输(严格次小生成树)
先跑个最小生成树,然后考虑加入每条非树边,在形成的环上去掉一条最大边,当然,要保证严格次小,还需要保留次小边,用倍增实现。
5. 最小生成树计数
有个性质:各个最小生成树的形态不同,但权值相同的边的数量一定相等。所以跑一遍最小生成树后直接暴力搜索,数据较小。
6. Tree
二分给白边加偏移量,然后跑最小生成树。