用缩点法证明kruskal和prim算法的正确性

n n n个点 m m m条边的无向带权连通图

kruskal

引理:所有边集中边权最小的边一定是最小生成树中的边

证明:对于任意一颗不含有最小边的最小生成树,把最小边加入生成树,会构成一个环,环上的边权值都比大于等于最小边,如果环上的最大值大于最小边,删掉最大边保留最小边可以让生成树权值减少,这与该生成树是最小生成树矛盾。因此得到环上边的权值都等于最小边,于是引理成立。

推论:每次拿出最小边 u , v u,v u,v,将 u , v u,v u,v合并成一个新的结点(加入 u , v u,v u,v后它们已经处于一个连通块),对应的边也合并,可以得到一个点数为 n − 1 n-1 n1的新图,问题转换为求新图的最小生成树,对新图继续应用引理,最后缩成一个点,就得到了最小生成树。

prim

引理:以 u u u为顶点的所有边中的最小边是最小生成树中的边。

证明:假设以 u u u为顶点的最小边是 u , v u,v u,v,对于任意不含有 u , v u,v u,v的最小生成树。加入边 u , v u,v u,v,此时构成了一个环,并且环上两条以 u u u为顶点的边,一条是 u , v u,v u,v,另一条权值一定大于等于 u , v u,v u,v的权值,如果另外一条大于,与这是一颗最小生成树矛盾,因此另外一条边的权值等于 u , v u,v u,v的权值,删除另外一条边保留 u , v u,v u,v,则这仍然是一颗最小生成树,引理得证。

推论:与krusakl的证明一样,反复应用引理即可

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用邻接矩阵实现图的算法和测试代码: ```c++ #include <iostream> #include <cstring> using namespace std; const int MAXN = 100; const int INF = 0x3f3f3f3f; int n; // 图中顶点数 int G[MAXN][MAXN]; // 邻接矩阵 // 普里姆算法生成最小生成树 void prim(int s) { int d[MAXN]; // 存储当前各个顶点到最小生成树的最短距离 bool vis[MAXN] = {false}; // 标记顶点是否已经在最小生成树中 memset(d, INF, sizeof(d)); // 初始化距离为无穷大 d[s] = 0; // 起点到自己的距离为0 for (int i = 0; i < n; i++) { int u = -1, min_d = INF; // 找到距离最小的顶点 for (int j = 0; j < n; j++) { if (!vis[j] && d[j] < min_d) { u = j; min_d = d[j]; } } if (u == -1) return; // 找不到可连接的顶点 vis[u] = true; // 将顶点加入最小生成树 for (int v = 0; v < n; v++) { if (!vis[v] && G[u][v] < d[v]) { d[v] = G[u][v]; // 更新最短距离 } } } } // 克鲁斯卡尔算法生成最小生成树 struct edge { int u, v, w; // 边的起点、终点和权值 bool operator<(const edge& E) const { return w < E.w; // 按照权值从小到大排序 } } edges[MAXN * MAXN]; // 存储所有边 int fa[MAXN]; // 并查集 int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); } void kruskal() { int cnt = 0; // 记录加入最小生成树的边数 for (int i = 0; i < n; i++) fa[i] = i; // 初始化并查集 sort(edges, edges + n * (n - 1) / 2); // 将边按照权值排序 for (int i = 0; i < n * (n - 1) / 2; i++) { int u = edges[i].u, v = edges[i].v, w = edges[i].w; int fu = find(u), fv = find(v); if (fu != fv) { // 如果不在同一个连通块中 fa[fu] = fv; // 合并连通块 cnt++; // 边数+1 if (cnt == n - 1) break; // 边数达到n-1,生成树完成 } } } // Dijkstra算法求单源最短路径 void dijkstra(int s, int d[]) { bool vis[MAXN] = {false}; // 标记顶点是否已经确定最短路径 memset(d, INF, sizeof(d)); // 初始化距离为无穷大 d[s] = 0; // 起点到自己的距离为0 for (int i = 0; i < n; i++) { int u = -1, min_d = INF; // 找到距离最小的顶点 for (int j = 0; j < n; j++) { if (!vis[j] && d[j] < min_d) { u = j; min_d = d[j]; } } if (u == -1) return; // 找不到可连接的顶点 vis[u] = true; // 将顶点加入最短路径 for (int v = 0; v < n; v++) { if (!vis[v] && G[u][v] != INF && d[u] + G[u][v] < d[v]) { d[v] = d[u] + G[u][v]; // 更新最短距离 } } } } int main() { cin >> n; memset(G, INF, sizeof(G)); // 初始化邻接矩阵为无穷大 for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { int w; cin >> w; if (w != -1) G[i][j] = w; // 如果有边,存储边的权值 } } // 测试普里姆算法 prim(0); // 测试克鲁斯卡尔算法 kruskal(); // 测试Dijkstra算法 int s; cin >> s; int d[MAXN]; dijkstra(s, d); // 输出最小生成树和最短路径 cout << "最小生成树:" << endl; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (G[i][j] != INF && find(i) == find(j)) { cout << i << " " << j << " " << G[i][j] << endl; } } } cout << "最短路径:" << endl; for (int i = 0; i < n; i++) { if (i == s) continue; cout << s << " " << i << " " << d[i] << endl; } return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值