最小生成树(采用Prim算法)
题目描述:设G =(V,E)是无向连通带权图,即一个网络。E中的每条边(v,w)的权为c[ v ] [ w ]。如果G的一个子图G’是一颗包含G的所有顶点的树,则称G’为G的生成树。生成树上各边权的总和称为该生成树的耗费。在G的所有生成树中,耗费最小的生成树称为G的最小生成树。
大白话:在一个无向图中,找到点数 - 1 个边可以连起来所有顶点,并且所有边权加起来最小。
下面以该题为例:
先看看Prim算法是如何进行描述,我们整体走一遍。从起点开始,选择未选进集合的最小的可达点。
图一
图二
图三
图四
图五
贪心策略:如果我们自己去找最小生成树,以我自己为例,可能每次我都会下意识的去找 对应那一点可以走的最小的点,但是走到第三张图的时候,我就无法再走下去了,所以贪心策略不是以当前点为中心,找到他可达周围的最小点。正确的贪心策略应该是,从目前可达的点中,选择一个最小的点去到达。所以,我们需要不断的去更新可达点的最小花费。
对起点v0,将可达点花费进行更新
选择v4,将可达点花费进行更新。对应图一
选择v5,将可达点花费进行更新。对应图二
选择v1,将可达点花费进行更新。对应图三
选择v2,将可达点花费进行更新。对应图四
选择v3,将可达点花费进行更新。对应图五
整体代码如下
public class Prim {
int [][] c; // 邻接矩阵
int [] closest; //记录最优连线
int [] lowcost; //记录目前可达点最小花费
boolean [] s; //判断点是否被选进集合
int n;//点集大小
public Prim(int n,int [][] c){
this.n = n;
this.c = c;
closest = new int[n + 1];
lowcost = new int[n + 1];
s = new boolean[n + 2];
prim(n,c,s,closest,lowcost);
}
public static void main(String[] args) {
int M = Integer.MAX_VALUE;
int[][] c = {
{0,34,M,M,12,M},
{34,0,46,M,M,19},
{M,46,0,17,M,25},
{M,M,17,0,38,25},
{12,M,M,38,0,26},
{M,19,25,25,26,0}
};
int n = c.length - 1;
new Prim(n,c);
}
public static void prim(int n, int [][] c,boolean [] s,int [] closest, int[] lowcost){
int cost = 0;
List<Integer> v = new ArrayList<>();
s[1] = true; //初始点默认为true
v.add(1);
//初始化第一个点的最小花费
for(int i = 1; i <= n; i++){
s[i+1] = false;
lowcost[i] = c[0][i]; //从 1 - i 的边权
closest[i] = 1;
}
//System.out.println(Arrays.toString(lowcost));
for(int i = 1; i <= n; i++){
int min = Integer.MAX_VALUE;
int j = 1;
//遍历 k可达的点
for(int k = 1; k <= n; k++){
if(lowcost[k] < min && !s[k + 1] ){
min = lowcost[k];
j = k;
}
}
// System.out.println((j) + " 号点选择边:" + lowcost[j]);
cost += lowcost[j];
s[j + 1] = true;
v.add(j+1);
//更新可达的点
for(int k = 1; k <= n; k++){
if(c[j][k] < lowcost[k] && !s[k + 1]){
lowcost[k] = c[j][k];
closest[k] = j + 1;
}
}
// System.out.println(Arrays.toString(lowcost));
}
//最小生成树点集
System.out.println("最接近点集" + v);
System.out.println("边集选择" + Arrays.toString(lowcost));
System.out.println("最小花费:" + cost);
}
}
/** 结果
最接近点集[1, 5, 6, 2, 3, 4]
边集选择[0, 19, 25, 17, 12, 26]
最小花费:99
*/