判断最小生成树的唯一性

Kruskal算法求最小生成树唯一性

先用kruskal算法算出最小生成树,并把最小生成树的边记录下来。然后依次枚举删除边,用其他的边再次使用kruskal算法算出最小生成树。如果算出的代价和原来的相同,则不唯一,否则唯一。另外当我们删除一条边之后,可能根本构不成一颗生成树,要判断一下。

代码如下:

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <queue>
#include <new>
#include <set>
#include <map>

using namespace std;

const int maxn = 1000;
int p[maxn], x[maxn], y[maxn], n;
struct edge
{
    int u, v, cost;
    bool operator < (const edge& b) const
    {
        return cost < b.cost;
    }
};
vector<edge> edges;
vector<int> mst_edges;

int find_root(int x)
{
    if (p[x] == -1) return x;
    return p[x] = find_root(p[x]);
}

void kruskal()
{
    memset(p, -1, sizeof(p));
    sort(edges.begin(), edges.end());

    mst_edges.clear();
    int cost = 0;
    for (int i = 0; i < edges.size(); i++)
    {
        int x = edges[i].u, y = edges[i].v;
        int rx = find_root(x);
        int ry = find_root(y);
        if (rx != ry)
        {
            p[rx] = ry;
            cost += edges[i].cost;
            mst_edges.push_back(i);
        }
    }

    printf("%d\n", cost);

    bool ok = false;
    for (int i = 0; i < mst_edges.size(); i++)
    {
        memset(p, -1, sizeof(p));
        int ans = 0, k = 0;
        for (int j = 0; j < edges.size(); j++)
        {
            if (mst_edges[i] == j) continue;
            int x = edges[j].u, y = edges[j].v;
            int rx = find_root(x);
            int ry = find_root(y);
            if (rx != ry)
            {
                p[rx] = ry;
                ans += edges[j].cost;
                k++;
            }
        }

        if (ans == cost && k == n - 1)//新算出的生成树的代价和最小生成树代价相同,且能构成一棵树(最小生成树的边数肯定是n - 1)
        {
            ok = true;
            break;
        }
    }

    if (ok) printf("Yes\n");
    else printf("No\n");
}

int main()
{
    //freopen("1.txt", "r", stdin);
    int T, Case = 0;
    scanf("%d", &T);
    while (T--)
    {
        scanf("%d", &n);
        edges.clear();
        for (int i = 1; i <= n; i++) scanf("%d%d", &x[i], &y[i]);
        for (int i = 1; i <= n; i++)
        for (int j = i + 1; j <= n; j++)
        {
            int d = abs(x[i] - x[j]) + abs(y[i] - y[j]);
            edges.push_back(edge{i, j, d});
        }

        printf("case #%d:\n", Case++);

        kruskal();
    }
    return 0;
}


在C语言中判断最小生成树唯一性是图论中的一个重要问题。判断最小生成树是否唯一,可依据最小生成树的存在条件和收录顶点时的情况来确定。如果图的连通集个数大于1,则最小生成树不存在;若图的连通集个数等于1,则最小生成树存在。在收录最小`dist`值至最小生成树中时,若最小`dist`值有多个,最小生成树唯一。 以下是实现该功能的C语言代码示例: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAXN 505 #define INF 0x3f3f3f3f typedef struct { int u, v, w; } Edge; Edge edges[MAXN * MAXN]; int parent[MAXN]; int n, m; // 比较函数,用于qsort int cmp(const void *a, const void *b) { return ((Edge *)a)->w - ((Edge *)b)->w; } // 查找节点的根节点 int find(int x) { if (parent[x] != x) { parent[x] = find(parent[x]); } return parent[x]; } // 合并两个集合 void unionSet(int x, int y) { int fx = find(x); int fy = find(y); if (fx != fy) { parent[fx] = fy; } } // 判断最小生成树是否唯一 int isMSTUnique() { int i, j, k; int mstWeight = 0; int mstEdgeCount = 0; int used[MAXN * MAXN] = {0}; int alternativeMSTWeight; int alternativeMSTEdgeCount; // 初始化并查集 for (i = 1; i <= n; i++) { parent[i] = i; } // 对边按权重排序 qsort(edges, m, sizeof(Edge), cmp); // 计算最小生成树的权重 for (i = 0; i < m; i++) { int u = edges[i].u; int v = edges[i].v; int w = edges[i].w; if (find(u) != find(v)) { unionSet(u, v); mstWeight += w; mstEdgeCount++; used[i] = 1; } } // 如果最小生成树的边数不等于n - 1,说明图不连通,最小生成树不存在 if (mstEdgeCount != n - 1) { return -1; } // 尝试寻找次小生成树 for (i = 0; i < m; i++) { if (used[i]) { // 初始化并查集 for (j = 1; j <= n; j++) { parent[j] = j; } alternativeMSTWeight = 0; alternativeMSTEdgeCount = 0; // 排除当前边,重新计算最小生成树 for (j = 0; j < m; j++) { if (j == i) continue; int u = edges[j].u; int v = edges[j].v; int w = edges[j].w; if (find(u) != find(v)) { unionSet(u, v); alternativeMSTWeight += w; alternativeMSTEdgeCount++; } } // 如果次小生成树的边数等于n - 1且权重等于最小生成树的权重,说明最小生成树唯一 if (alternativeMSTEdgeCount == n - 1 && alternativeMSTWeight == mstWeight) { return 0; } } } // 最小生成树唯一 return 1; } int main() { int i; // 输入顶点数和边数 scanf("%d %d", &n, &m); // 输入每条边的信息 for (i = 0; i < m; i++) { scanf("%d %d %d", &edges[i].u, &edges[i].v, &edges[i].w); } int result = isMSTUnique(); if (result == -1) { printf("No MST\n"); } else if (result == 0) { printf("No\n"); } else { printf("Yes\n"); } return 0; } ``` ### 代码解释 1. **输入处理**:先读取无向图的顶点数`n`和边数`m`,再读取每条边的两个端点和权重,存储在`edges`数组中。 2. **并查集操作**:`find`函数用于查找节点的根节点,`unionSet`函数用于合并两个集合。 3. **排序**:使用`qsort`函数对边按权重从小到大排序。 4. **计算最小生成树的权重**:遍历排序后的边,若边的两个端点不在同一集合中,则将该边加入最小生成树,更新权重和边数。 5. **判断最小生成树是否唯一**:尝试排除最小生成树中的每条边,重新计算最小生成树。若得到的次小生成树的权重等于最小生成树的权重,则最小生成树唯一。 6. **输出结果**:根据判断结果输出相应信息。 ### 复杂度分析 - **时间复杂度**:排序边的时间复杂度为$O(m log m)$,每次计算最小生成树的时间复杂度为$O(m \alpha(n))$,其中$\alpha(n)$是阿克曼函数的反函数,接近常数。因此,总的时间复杂度为$O(m log m + m^2 \alpha(n))$。 - **空间复杂度**:主要使用了并查集和存储边的数组,空间复杂度为$O(m + n)$。
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值