1. 程式人生 > >The Unique MST POJ

The Unique MST POJ

從前有兩隻貓。它們實在不知道該出什麼題了。 於是它們放棄了治療,開始玩一個遊戲:從鄉鎮地圖中已有的道路里面刪除一些道路,並且刪除完畢後圖仍然是連通的。在所有方案中,刪除道路總長度最大的方案為最優方案。 兩隻貓同時完成了這個遊戲。它們都堅信自己是贏家。已知它們的完成方式不同,請判斷有沒有可能它們的實現方案都是最優的。

Input

第一行是一個整數 t (1 <= t <= 20), 測試用例的數量。每個用例代表一張圖,第一行是n和m (1 <= n <= 100), 分別為城鎮數和道路數。接下來m行為m個三元組 (xi, yi, wi),表示編號為xi和yi的城鎮被長度為wi的道路連線。兩個城鎮之間最多被一條道路連線。

Output

   對於每個用例,如果答案為否定(即不可能都是最優方案),輸出最優方案剩餘的(注意不是刪除的)道路總長度。否則輸出字串 '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!

題解:這題要用到求次小生成樹的方法。具體就是在求最小生成樹的過程中把從點i到j的生成樹上的邊的最大權值記錄下來,用maxw[i][j]表示,那麼在最小生成樹上如果再加任意一條邊的話都會形成環,於是要讓圖成為生成樹的話就必須刪掉一條邊(除加上的邊以為),那麼刪的這條邊必須是原來i,j兩點的最大邊,才有可能次小,如果求出來的要刪的次小邊與新添的邊長度一樣,那麼就說明最小生成樹不止一個。

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 110;
int g[maxn][maxn],d[maxn],vis[maxn],link[maxn][maxn],maxw[maxn][maxn],pre[maxn],n,m;
//link[i][j] 判斷i,j是否可以連通,pre[i]表示在生成樹中i的前一點是哪個

int prim()
{
    memset(d,inf,sizeof(d));
    memset(vis,0,sizeof(vis));
    memset(maxw,0,sizeof(maxw));
    for(int i = 1;i <= n;i++) pre[i] = 1;
    d[1] = 0;

    int sum = 0;
    while(1)
    {
        int v = -1;
        for(int u = 1;u <= n;u++)
        {
            if(!vis[u] && (v == -1 || d[v] > d[u])) v = u;
        }
        if(v == -1) break;
        vis[v] = 1;
        sum += d[v];
        int pred = pre[v];
        maxw[pred][v] = d[v];
        for(int i = 1;i <= n;i++)//更新maxw[i][v]
        {
            maxw[i][v] = max(maxw[pred][v],maxw[i][pred]);
        }
        link[pred][v] = link[v][pred] = 0;//已經在樹中的點要標記為不可連通
        for(int u = 1;u <= n;u++)
        {
            if(!vis[u] && d[u] > g[v][u])
            {
                d[u] = g[v][u];
                pre[u] = v;
            }
        }
    }
    return sum;
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        memset(link,0,sizeof(link));
        memset(g,inf,sizeof(g));

        scanf("%d %d",&n,&m);
        for(int i = 1;i <= m;i++)
        {
            int u,v,w;
            scanf("%d %d %d",&u,&v,&w);
            g[u][v] = g[v][u] = w;
            link[u][v] = link[v][u] = 1;
        }
        int ans = prim();
        int flag = 0;

        for(int i = 1;i <= n;i++)
        {
            for(int j = 1;j <= n;j++)
            {
                if(!link[i][j] || g[i][j] == inf) continue;
                if(g[i][j] == maxw[i][j])//要刪的邊和加的邊權值相等
                    flag = 1;
            }
        }

        if(flag)
            printf("Not Unique!\n");
        else
            printf("%d\n",ans);
    }
    return 0;
}