POJ 1679 The Unique MST(次小生成树O(mlogm) )

Description

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!’.

考虑最小生成树的唯一性。如果一条边 不在最小生成树的边集中 ,并且可以替换与其 权值相同、并且在最小生成树边集 的另一条边。那么,这个最小生成树就是不唯一的。
对于 Kruskal 算法,只要计算为当前权值的边可以放几条,实际放了几条,如果这两个值不一样,那么就说明这几条边与之前的边产生了一个环(这个环中至少有两条当前权值的边,否则根据并查集,这条边是不能放的),即最小生成树不唯一。
寻找权值与当前边相同的边,我们只需要记录头尾指针,用单调队列即可在 O ( α ( m ) ) O(α(m)) O(α(m)) (m 为边数)的时间复杂度里优秀解决这个问题(基本与原算法时间相同)。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>

using namespace std;
const int N = 500007;

int n, m;
int fa[N];
struct node{
    
    
    int x, y, z;
    bool operator<(const node &t)const {
    
    
        return z < t.z;
    }
}e[N];

int find(int x){
    
    
    if(fa[x] == x)return x;
    return fa[x] = find(fa[x]);
}

int main(){
    
    
    int t;
    scanf("%d", &t);
    while(t -- ){
    
    
        int cnt = 0;
        scanf("%d%d", &n, &m);
        for(int i = 0; i <= n; ++ i)
            fa[i] = i;

        for(int i = 1; i <= m; ++ i){
    
    
            int x, y, z;
            scanf("%d%d%d", &x, &y, &z);
            e[++ cnt] = (node){
    
    x, y, z};
        }
        sort(e + 1, e + 1 + cnt);
        //sum1为相同权值大小 可以 添加到最小生成树中的边数(当前还没放呢)
        //sum2为相同权值大小 实际 添加到最小生成树中的边数
        //如果最小生成树唯一,则sum1 == sum2
        int tail = 0, ans = 0, sum1 = 0, sum2 = 0, num = 0;
        bool flag = 1;
        for(int i = 1; i <= m + 1; ++ i){
    
    
            if(i > tail){
    
    //开始新的一个权值的边以后
                if(sum1 != sum2){
    
    
                    flag = 0;
                    break;
                }
                sum1 = 0;
                //要到 m+1 才能把 m 条边都扫一遍
                for(int j = i; j <= m + 1; ++ j){
    
    
                    if(e[i].z != e[j].z){
    
    
                        tail = j - 1;
                        break;
                    }
                    if(find(e[j].x) != find(e[j].y))
                        sum1 ++ ;
                }
                sum2 = 0;
            }

            if(i > m)break;

            int x = find(e[i].x);
            int y = find(e[i].y);
            if(x != y && num != n - 1){
    
    
                fa[x] = y;
                sum2 ++ ;
                num ++ ;
                ans += e[i].z;
            }
        }
        if(flag)
            printf("%d\n", ans);
        else printf("Not Unique!\n");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_45697774/article/details/108552652