POJ 1679 The Unique MST 【最小生成树/次小生成树模板】

 The Unique MST

Time Limit: 1000MS   Memory Limit: 10000K
Total Submissions: 22668   Accepted: 8038

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

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!

 【次小生成树】:

重要的是理解求次小生成树的过程。求次小生成树建立在Prim算法的基础上。可以确定的是,次小生成树肯定是由最小生成树删去一条边再加上一条边得到。那么我们应该删去哪条边再加上哪条边呢?假设两点u,v之间有一条边且这条边不在MST中,那么可以尝试加上这条边。但是加上这条边以后会出现环,则一定要去掉回路上的一条边,这条边应该选择回路上权值最大的那条边(毕竟权值要求尽量小)。尝试对每对点对进行上述操作,那么最小的那个结果就是次小生成树。

#include<cstdio>
#include<string>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<cstring>
#include<set>
#include<queue>
#include<algorithm>
#include<vector>
#include<map>
#include<cctype>
#include<stack>
#include<sstream>
#include<list>
#include<assert.h>
#include<bitset>
#include<numeric>
#define debug() puts("++++")
#define gcd(a,b) __gcd(a,b)
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define fi first
#define se second
#define pb push_back
#define sqr(x) ((x)*(x))
#define ms(a,b) memset(a,b,sizeof(a))
#define sz size()
#define be begin()
#define pu push_up
#define pd push_down
#define cl clear()
#define lowbit(x) -x&x
#define all 1,n,1
#define rep(i,x,n) for(int i=(x); i<=(n); i++)
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> P;
const int INF = 0x3f3f3f3f;
const LL LNF = 1e18;
const int maxm = 1e6 + 10;
const double PI = acos(-1.0);
const double eps = 1e-8;
const int dx[] = {-1,1,0,0,1,1,-1,-1};
const int dy[] = {0,0,1,-1,1,-1,1,-1};
int dir[4][2] = {{0,1},{0,-1},{-1,0},{1,0}};
const int mon[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
const int monn[] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
const int mod = 10056;
#define inf 0x3f3f3f3f
#define ll long long
const int maxn = 10010;

int u,v,w;
int n,m,ans,k,sum,cnt;
struct node
{
    int u,v,w;
}e[maxn];
int fa[maxn];
int mst[maxn];
int Find(int x)
{
    if(fa[x]!=x)
        fa[x]=Find(fa[x]);
    return fa[x];
}
void join(int x,int y)
{
    int xx = Find(x);
    int yy = Find(y);
    fa[xx]=yy;
}
bool cmp(node a,node b)
{
    return a.w<b.w;
}
void kruskal()
{
    rep(i,1,n-1)  //枚举mst内的n-1个点
    {
        cnt=0,sum=0;
        rep(j,1,n) fa[j]=j; //每次枚举待删边 重新设置
        rep(j,1,m)
        {
            if(j != mst[i]) //枚举不在mst中的每一条边
                if(Find(e[j].u) != Find(e[j].v))
                {
                    join(Find(e[j].u), Find(e[j].v));
                    sum += e[j].w;
                    cnt++;
                }
        }
        if(cnt==n-1 && sum==ans) //最小=次小——>不唯一
        {
             printf("Not Unique!
");
             return ;
        }
    }
    printf("%d
",ans);
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        sum=0,k=0,ans=0;
        scanf("%d %d",&n, &m);
        for(int i=1;i<=n;i++)
            fa[i]=i;
        for(int i=1; i<=m; i++)
            scanf("%d %d %d",&e[i].u, &e[i].v, &e[i].w);
        sort(e+1,e+m+1,cmp);
        for(int i=1;i<=m;i++) //kruskal造mst数组存储其中包含的边的编号
        {
            int x=Find(e[i].u);
            int y=Find(e[i].v);
            if(x != y)
            {
                join(x,y);
                ans+=e[i].w;
                mst[++k]=i; //先生成mst,存储mst的每一条边
            }
        }
       kruskal(); //然后枚举删除每一条mst中的边,看是否能找出一课次小生成树的权值和最小生成树的权值相等。
       //如果相等,则不唯一
    }
}
/*
【题意】
给你n个点m条边的图,判断图的最小生成树是否唯一。

【类型】
次小生成树

【分析】
首先求出最小生成树的结果sum,并记录每条边是否属于最小生成树,如果存在和sum相同的生成树,则此生成树一定包含不属于最小生成树的边,
即枚举每条不属于最小生成树的边,对每条边求最小生成树,如果结果和sum相同,则not unique,否则最小生成树唯一,输出sum. 【时间复杂度&&优化】 16ms 【trick】
*/
原文地址:https://www.cnblogs.com/Roni-i/p/9420630.html