K - The Unique MST(Kruskal判断最小生成树是否唯一)

本文介绍了一种算法,用于判断给定的无向连通图的最小生成树是否唯一。通过构造图并进行最小生成树计算,接着移除权值相同的边再次计算,比较两次结果来判断最小生成树的唯一性。
Given a connected undirected graph, tell if its minimum spanning tree is unique.

Definition 1 (Spanning Tree): Consider a connected, undirected graph G = (V, E). A spanning tree of G is a subgraph of G, say T = (V', E'), with the following properties:
1. V' = V.
2. T is connected and acyclic.

Definition 2 (Minimum Spanning Tree): Consider an edge-weighted, connected, undirected graph G = (V, E). The minimum spanning tree T = (V, E') of G is the spanning tree that has the smallest total cost. The total cost of T means the sum of the weights on all the edges in E'.

Input
The first line contains a single integer t (1 <= t <= 20), the number of test cases. Each case represents a graph. It begins with a line containing two integers n and m (1 <= n <= 100), the number of nodes and edges. Each of the following m lines contains a triple (xi, yi, wi), indicating that xi and yi are connected by an edge with weight = wi. For any two nodes, there is at most one edge connecting them.
Output
For each input, if the MST is unique, print the total cost of it, or otherwise print the string ‘Not Unique!’.
Sample Input

2
3 3
1 2 1
2 3 2
3 1 3
4 4
1 2 2
2 3 2
3 4 2
4 1 2

Sample Output

3
Not Unique!

判断最小生成树是否唯一, 对构造好的图进行一次MST,然后依次去除MST中权值相同的边,再进行一次MST,如果两次最小权值和相同,则MST不唯一

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
struct node
{
  int u, v, w;
  bool used, del, equ;//是否访问过,删除的边,相等的边
}t[121212];
int pre[121212];//记录父节点
int n, m;
bool flag;
int root(int a)//寻找父节点
{
  if(pre[a]==a)
  return a;
  return pre[a] = root(pre[a]);
}
bool cmp(struct node a, struct node b)//快排
{
  return a.w < b.w;
}
int kruskal()//最小生成树
{
   int sum = 0;
   int k = n;
   for(int i=0;i<300;i++)
   {
      pre[i] = i;
   }
   for(int i=0;i<m&&k>1;i++)
   {
     if(t[i].del)//边是否被删除
     continue;
     if(root(t[i].u)!=root(t[i].v))
     {
       pre[root(t[i].v)] = root(t[i].u);
       sum += t[i].w;
       if(flag)
       t[i].used = true;
       k--;
     }
   }
   return sum;
}
int main()
{
   int T, u, v, w;
   cin>>T;
   while(T--)
   {
     cin>>n>>m;
     for(int i=0;i<m;i++)
     {
        cin>>u>>v>>w;
        t[i].u = u - 1;//从0开始
        t[i].v = v - 1;
        t[i].w = w;
        t[i].used = t[i].del = t[i].equ = false;
     }
     for(int i=0;i<m;i++)
     {
       for(int j=0;j<m;j++)
       {
         if(i==j)
         continue;
         else if(t[i].w==t[j].w)
         t[i].equ = true;//有相同权值的边
       }
     }
     sort(t, t + m, cmp);//快排
     flag = true;
     int num1 = kruskal(), num2;//分别记录两次的权值
     flag = false;
     bool flag1 = true;//记录是否唯一
     for(int i=0;i<m;i++)
     {
        if(t[i].used&&t[i].equ)
        {
            t[i].del = true;//删除此边
            num2 = kruskal();
            if(num1==num2)//权值相等,不唯一
            {
              cout<<"Not Unique!"<<endl;
              flag1 = false;
              break;
            }
            t[i].del = false;//恢复此边
        }
     }
     if(flag1)
     cout<<num1<<endl;
   }
   return 0;
}
带权无向图的最小生成树MST)并不一定是唯一的。其唯一性取决于图中是否存在多条权值相同的边,这些边在构建生成树时可以被互换而不影响最终生成树的总权值。 在最小生成树的构造过程中,无论是使用 Prim 算法还是 Kruskal 算法,核心思想都是选择当前可选的最小权值边,并确保不形成环[^1]。如果在某个阶段存在多个权值相同的候选边,并且它们都不会形成环,则可以选择其中任意一条边加入生成树。这种情况下,不同的选择可能导致生成结构不同但总权值相等的多个最小生成树。 例如,在 Kruskal 算法中,若图中有若干边具有相同权值,并且这些边连接的是尚未连通的顶点对,则每一种选择都可能产生一个不同的生成树结构,但它们都满足最小生成树的定义,即总权值为最小值[^2]。 同样地,在 Prim 算法中,当从两个或多个相邻顶点到已生成树的距离(边权值)相同时,算法可以自由选择其中一个顶点加入生成树,从而导致生成树结构的多样性[^3]。 因此,总结如下: - 若图中所有边的权值均不相同,则最小生成树唯一的。 - 若图中存在多个相同权值的边,并且这些边可以在不形成环的前提下以不同方式组合形成生成树,则最小生成树可能有多个。 ### 示例代码:检测最小生成树是否唯一 可以通过修改 Kruskal 或 Prim 算法来检测最小生成树是否唯一。以下是一个基于 Kruskal 算法的思想示例: ```python def is_mst_unique(graph): edges = sorted(graph.edges, key=lambda x: x.weight) parent = {v: v for v in graph.vertices} def find(u): while parent[u] != u: parent[u] = parent[parent[u]] u = parent[u] return u def union(u, v): root_u = find(u) root_v = find(v) if root_u == root_v: return False parent[root_v] = root_u return True total_weight = 0 unique_edges = set() used_edges = [] for edge in edges: if union(edge.u, edge.v): total_weight += edge.weight used_edges.append(edge) # Now try to replace each used edge with another of same weight for i, edge in enumerate(used_edges): temp_parent = parent.copy() temp_total = total_weight - edge.weight temp_used = used_edges[:i] + used_edges[i+1:] temp_graph = [(e.u, e.v, e.weight) for e in edges if e != edge] temp_graph_sorted = sorted(temp_graph, key=lambda x: x[2]) temp_uf_parent = {v: v for v in graph.vertices} def temp_find(u): while temp_uf_parent[u] != u: temp_uf_parent[u] = temp_uf_parent[temp_uf_parent[u]] u = temp_uf_parent[u] return u def temp_union(u, v): root_u = temp_find(u) root_v = temp_find(v) if root_u == root_v: return False temp_uf_parent[root_v] = root_u return True temp_sum = temp_total temp_count = 0 for e in temp_graph_sorted: if temp_union(e[0], e[1]): temp_sum += e[2] temp_count += 1 if temp_count == len(graph.vertices) - 1: break if temp_sum == total_weight and temp_count == len(graph.vertices) - 1: return False # A different MST exists with the same total weight return True # All MSTs are unique ``` ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值