算法设计第二次上机作业-The Unique MST

唯一MST树判断与Kruskal算法
本文深入解析如何使用Kruskal算法判断一个图的最小生成树(MST)是否唯一,通过并查集实现连通性检查,确保算法正确性。分享了完整的AC代码及常见错误排查,为理解MST树的唯一性提供了实用指南。
           # 算法设计第二次上机作业-The Unique MST
题目描述

判断一个图的MST树是否唯一

输入
第一行是输入的测试样例的个数
后面的每一个样例的第一行都给出两个数n和m,给出了总节点数和边数。
之后的m行给出每条边的两个节点和权重。

输出
如果MST唯一,则输出权重
若不唯一,则输出"Not Unique!"

思路:

查找MST树用Krustal算法,将所有的边进行排序,每次都选择权重最小的边,判断这条边和已经在mst_edges中的边是否连通,若不连通则加入到集合中。
判断连通则是用并查集,并查集可以看这篇博客并查集详解

AC的代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
#define Max 10005
using namespace std;
struct edge {
    int to;
    int from;
    int weight;
};
int p[Max]; //记录每个节点的上级节点(不是祖先)
edge edges[Max];
vector<int> mst_edges;

int cmp(edge edge1, edge edge2) {
    return edge1.weight < edge2.weight;
}

int find_root(int x) {	//并查集找到每一个节点的祖先节点
    if(p[x]!=-1) {
        return find_root(p[x]);
    }
    else {
        return x;
    }
}

void Krustal(int n, int m) {
    int num = 0, weight = 0;
    memset(p, -1, sizeof(p));
    mst_edges.clear();
    for(int i=0; i<m; i++) {
        if(num == n-1) break;
        int px = find_root(edges[i].from);
        int py = find_root(edges[i].to);
        if(px != py) {	//这条边和其他的边不连通
            p[px] = py;
            num++;
            weight += edges[i].weight;
            mst_edges.push_back(i);
        }
    }
    bool flag = true;
    int size = mst_edges.size();
    for(int i=0; i<size; i++) {	//去掉mst_edges中的每一条边,看剩下的边能否构成一棵MST树
        int ans = 0;
        num = 0;
        memset(p, -1, sizeof(p));
        for(int j=0; j<m; j++) {
            if(mst_edges[i] == j) continue;
            int px = find_root(edges[j].from);
            int py = find_root(edges[j].to);
            if(px != py) {
                p[px] = py;
                ans += edges[j].weight;
                num++;
            }
        }
        if(num == n-1 && ans == weight) {	//如果剩下的边能够构成一棵树并且权重和之前的相同,说明不唯一
            flag = false;
            break;
        }
    }
    if(flag == true) cout<<weight<<endl;
    else cout<<"Not Unique!"<<endl;
}

int main(int argc, const char * argv[]) {
    // insert code here...
    int num, n, m;
    scanf("%d",&num);
    for(int i=0; i<num; i++) {
        scanf("%d%d", &n, &m);
        for(int j=0; j<m; j++) {
            scanf("%d%d%d", &edges[j].from, &edges[j].to, &edges[j].weight);
        }
        sort(edges, edges+m, cmp);
        Krustal(n, m);
    }
    return 0;
}
出现的bug有:
  1. 中间出现过TEL,因为Max值设小了,所以如果值设小了不一定会出现Time Error
  2. 判断是否是树的时候一开始写成了边数-1,应该是节点数-1
<think>嗯,用户现在问的是Karger-Klein-Tarjan算法在最小生成树(MST)中的应用,特别是实现和原理。我需要先确认用户提到的这个算法是否确实应用于MST。之前我的回答是关于该算法在最小割问题中的应用,用户可能混淆了不同的问题,或者可能Karger-Klein-Tarjan确实有MST相关的工作。 首先,我要回想Karger、Klein和Tarjan三位学者是否有关于MST算法。记得Karger和Tarjan确实合作过MST相关的随机化算法,可能用户将Karger-Klein-Tarjan算法误认为是用于MST的,或者他们确实有一个MST算法如,Karger, Klein, Tarjan在1995年发表过一篇关于随机线性时间MST算法的论文,这可能就是用户所指的。 接下来,用户引用的内容提到MST和Prim算法,可能他们之前看到的相关资料中涉及到了这些内容。不过用户现在明确转向询问Karger-Klein-Tarjan在MST中的应用,因此需要将重点放在该算法MST版本上。 需要确认算法的核心思想:随机采、递归分解、边权过滤等。如,Karger等人的MST算法可能通过随机采边,然后递归地在子图中找到MST的候选,再验证并合并结果,类似于他们处理最小割的方法。 同时,用户提供的引用中的内容涉及到MST的性质,比如替换边来保持MST的结构,这可能与算法中的某些步骤相关,比如如何安全地删除不需要的边而不影响最终结果。 需要注意用户可能的混淆点,比如区分最小割和最小生成树的不同应用场景,但根据用户的问题,必须围绕MST展开回答。同时,需要确保算法的步骤和数学基础正确,比如时间复杂度的分析,以及如何通过随机采保证正确性。 最后,生成相关问题时要围绕MST算法的实现细节、正确性证明、与其他算法的比较等方面展开,帮助用户深入理解。</think>### Karger-Klein-Tarjan MST算法原理与实现 Karger、Klein和Tarjan在1995年提出的**随机化线性时间算法**是首个能够在$O(m + n)$期望时间内求解无向图最小生成树(MST)的突破性成果[^1]。其核心思想结合**边权过滤**与**递归验证**,通过概率方法减少需处理的边数。 --- #### 核心步骤 1. **随机边采与Borůvka迭代** 通过多轮Borůvka算法收缩连通分量,每轮随机选择部分边加入候选集,满足: - 每轮边数减少至$O(n \log n)$ - 保留所有可能属于MST的边 2. **递归分解与验证** 将图分解为更小的子图,递归计算子图MST,再合并结果。关键性质: - 若边$e$在子图MST中,则它在原图MST中可能被保留 - 通过**权值比较**过滤冗余边 3. **线性时间保证** 通过几何级数式边数缩减,确保每层递归处理边数总和为$O(m)$,总时间复杂度为$O(m + n)$。 --- #### 数学基础 - **安全边判定**:对边$e=(u,v)$,若存在一条路径$u \leadsto v$且路径上所有边权值$\leq w(e)$,则$e$不属于任何MST(类似于引用中提到的替换原理) - **采概率分析**:每轮保留边的概率设计保证至少$1/2$的概率覆盖所有关键边 --- #### 算法实现(伪代码) ```python def kkt_mst(G): if |V(G)| ≤ 2: return G.edges # Borůvka收缩阶段 F = set() while存在未收缩的连通分量: 对每个连通分量选择最小权边加入F 收缩所有连通分量得到新图G' # 随机采过滤 E_sampled = 对G中未被F覆盖的边,以概率1/2采 E_candidate = F ∪ E_sampled # 递归验证 MST_sub = kkt_mst(由E_candidate导出的子图) return MST_sub ∪ 必要的Borůvka边 ``` --- ### 与Prim/Kruskal算法的区别 | 特性 | Karger-Klein-Tarjan | Prim/Kruskal | |-------------------|------------------------------|---------------------| | 时间复杂度 | $O(m + n)$期望时间 | $O(m \log n)$ | | 空间复杂度 | $O(m)$ | $O(m)$ | | 核心方法 | 随机采+递归验证 | 贪心选择 | | 适用场景 | 超大规模稀疏图 | 中小规模图 | --- ### 应用场景 1. **网络优化**:电信骨干网布线成本最小化 2. **聚类分析**:基于相似度的层次聚类结构构建 3. **图像分割**:像素区域连通性建模 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值