题目
给定一个无向带权连通图,用贪心法求解这个图的最小生成树,要求如下:
(1)输入:用户给出顶点个数n,给出两个顶点之间的边的权值。
(2)输出:构成最小生成的边,以及对应的最小耗费。
最小生成树定义:
一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。
两个求最小生成树的经典算法:
1、prim(普里姆)算法(又称加点法)
2、kruskal(克鲁斯卡尔)算法(又称加边法)
贪心算法定义:
在对问题求解时,总是做出在当前看来是最好的选择。
贪心算法求解问题的条件:
1、贪心选择性质——所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。这是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别。
2、最优子结构——一个问题的最优解包含其子问题的最优解。
最小生成树的最优子结构证明:
(1)W(T)=W(T1)+W(T2)+W(u,v)
(2)如果可以为G1找到一个耗费比T1更小的生成树T1’,则我们可以为图G构造一个新的生成树T’={T1’}∪{T2} ∪{(u,v)},它的耗费要比T更小,这和T是G的最小生成树是矛盾的。
(3)所以:T1是子图G1的最小生成树。
最小生成树的贪心选择性质证明:
假设:已知一个图的最小生成树T如图所示,边(u,v)是连接U和V-U的权值最小的边,但是没有被包含T中。
(1)在u到v的简单路径上找到一条过渡边(u ’,v ’ ),它的权值c[u ’][v ’ ]> c[u ][v ]。
(2)用边(u ,v )替代(u ’,v ’ ),可以生成一个新的生成树T ’ ,它的耗费比T小,这和假设是矛盾的。
贪心选择策略:
每次为子集U找一条最小的边连接到{V-U}的顶点(局部最优),最终实现全局最优。
kruskal(克鲁斯卡尔)算法基本思想如下:
第一步:把G=(V,E)的所有边按权值由小到大排序。
第二步:初始时最小生成树T=空集,依次扫描各边,
对每条边作如下操作:
(1)如果在T中加入该边不构成回路,则加入该边;
(2)否则,放弃该边。直到T中包含了n-1条边,得到一个最小生成树。
4.Kruskal算法求最小生成树过程图
4.Kruskal算法—算法实现过程
(1)将G的n个顶点看成n个孤立的连通分支。
(2)将所有的边按权从小到大排序(为了做出基于当前的最好选择)。
(3)从第一条边开始,依次查看每一条边,并按下述方法连接2个不同的连通分支:
当查看到第k条边(v,w)时,如果端点v和w分别是当前2个不同的连通分支T1和T2中的顶点时,就用边(v,w)将T1和T2连接成一个连通分支,然后继续查看第k+1条边;如果端点v和w在当前的同一个连通分支中,就直接再查看第k+1条边。这个过程一直进行到只剩下一个连通分支时为止。
具体代码如下:
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#define INF 999
/**************用无向带权邻接矩阵来存储无向连通带权图*************************/
typedef struct {
int weight;//权值
}ArcNode;
typedef struct {
char node[10];//顶点向量
ArcNode arcs[10][10];//权重
int m,n;//顶点数和的边数
}AdjMatrix;
//找顶点v在顶点数组中的下标
int LocateVex(AdjMatrix *t,char v){
for (int i = 0; i < t->m; i++)
if (t->node[i] == v)
return i;
return -1;
}
//初始化顶点矩阵
void creat(AdjMatrix* t){
int i,j;
printf("please input the vexnum and arcnum:");//顶点数和边数
scanf("%d%d",&t->m,