1. 程式人生 > >7-14 最小生成樹的唯一性(30 分) 生成樹綜合練習題

7-14 最小生成樹的唯一性(30 分) 生成樹綜合練習題

題目描述:

7-14 最小生成樹的唯一性(30 分)

給定一個帶權無向圖,如果是連通圖,則至少存在一棵最小生成樹,有時最小生成樹並不唯一。本題就要求你計算最小生成樹的總權重,並且判斷其是否唯一。

輸入格式:

首先第一行給出兩個整數:無向圖中頂點數 N500)和邊數 M。隨後 M 行,每行給出一條邊的兩個端點和權重,格式為“頂點1 頂點2 權重”,其中頂點從 1 到N 編號,權重為正整數。題目保證最小生成樹的總權重不會超過 230

輸出格式:

如果存在最小生成樹,首先在第一行輸出其總權重,第二行輸出“Yes”,如果此樹唯一,否則輸出“No”。如果樹不存在,則首先在第一行輸出“No MST”,第二行輸出圖的連通集個數。

輸入樣例 1:

5 7
1 2 6
5 1 1
2 3 4
3 4 3
4 1 7
2 4 2
4 5 5

輸出樣例 1:

11
Yes

輸入樣例 2:

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

輸出樣例 2:

4
No

輸入樣例 3:

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

輸出樣例 3:

No MST
2


賽後分析:比賽的時候根本沒有看這道題,因為一想到自己生成樹都忘記怎麼寫了就直接扔了這道題,後來補題的時候發現,這算是一道比較綜合的生成樹的題目了,應該學習一下

題目分析:求最小生成樹,並判斷是不是唯一的,可以直接先求出次小生成樹,看看相不相等,如果相等,那麼最小生成樹肯定不唯一啊,同時這道題還要判斷存不存在最小生成樹,用並查集即可判斷。

AcCode:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 510;
typedef long long LL;
const int INF = 0x3f3f3f3f;
int N, M;
bool vis[maxn];              //求最小生成樹的時候用來標記是不是已經走過
bool used[maxn][maxn];       //求次小生成樹的時候用
int Map[maxn][maxn];         //儲存任意兩條邊之間的關係
int dis[maxn];               //儲存邊的距離
int pre[maxn];               //儲存之前的點
int MaxP[maxn][maxn];        //i 到 j 之間最大的距離
int f[maxn];                 //並查集求連通個數

int prim()                                    //prim演算法求最小生成樹
{
    for(int i = 1; i <= N; i++){
        dis[i] = Map[i][1];
        pre[i] = 1;
    }
    dis[1] = 0;
    vis[1] = true;
    int ans = 0;

    for(int i = 1; i < N; i++){
        int temp = INF, pos;
        for(int j = 1; j <= N; j++){
            if(!vis[j] && temp > dis[j]){
                temp = dis[j], pos = j;
            }
        }
        if(temp == INF)
            return -1;
        used[pre[pos]][pos] = used[pos][pre[pos]] = true;
        ans += dis[pos];
        vis[pos] = true;
        for(int j = 1; j <= N; j++){
            if(vis[j] && j != pos)
                MaxP[pos][j] = MaxP[j][pos] = max(MaxP[j][pre[pos]], dis[pos]);
            if(!vis[j]){
                if(dis[j] > Map[pos][j]) {
                    dis[j] = Map[pos][j];
                    pre[j] = pos;
                }
            }
        }
    }
    return ans;
}

void init()
{
    for(int i = 1; i <= N; i++)   //並查集的初始化
        f[i] = i;
    memset(Map, 0x3f, sizeof(Map));
    memset(vis, 0, sizeof(vis));
    memset(dis, 0x3f, sizeof(dis));
    memset(MaxP, 0, sizeof(MaxP));
    return ;
}
int Find(int x)
{
    if(x == f[x])
        return x;
    else
        return f[x] = Find(f[x]);
}

void unit(int a, int b)
{
    int fa = Find(a);
    int fb = Find(b);
    if(fa == fb)
        return ;
    else {
        if(fa < fb) f[fb] = fa;
        else        f[fa] = fb;
    }
    return ;
}

int main()
{
    scanf("%d %d", &N, &M);
    init();
    while(M--){
        int v1, v2, w;
        scanf("%d %d %d", &v1, &v2, &w);
        unit(v1, v2);
        Map[v1][v2] = Map[v2][v1] = w;
    }
    int x = prim();
    if(x == -1){              //如果不連通
        cout << "No MST" << endl;
        int sum = 0;
        for(int i = 1; i <= N; i++){
            if(i == Find(i)) sum++;}
        cout << sum << endl;
    }
    else {
        cout << x << endl;
        int ans = INF;
        for(int i = 1; i <= N; i++){
            for(int j = 1; j <= N; j++){
                if(i != j && !used[i][j])
                    ans = min(ans, x+Map[i][j]-MaxP[i][j]);
            }
        }
        if(ans == x) cout << "No" << endl;
        else cout << "Yes" << endl;
    }
    return 0;
}