POJ 1679 最小生成树

题意很简单:给你一些点和边的关系,求最小生成树是否是唯一的,若不唯一,则输出最小生成树的长度。

解题思路:在每次找到一条边之后,看这个新找到的点是否只从一个已经找过的点延伸过来,如果能找到两个以上的点,那么说明最小生成树不止一个,退出判断即可。


但是我一开始在做这道题的时候,思路却是:找到一个新的点P后,从这个原来的点集P'中能否找到一个和新的距离相等的点Q。这个想法之所以错误,是因为P'Q可能也是最小生成树中的一部分,现在你就把他找出来判定是没意义的。比如这组数据:

1
3 2
1 2 1
1 3 1
如果按照我的错误做法,输出的是

Not Unique!
但实际上这组数据的最小生成树是唯一的,应该输出2。我的错误点就在于,在第一遍找到点2后,判断是否唯一时发现dist[3] == dist[2],然后就把3误以为是重了。

而正确的解题思路是从已经找过的点里面去找是否唯一,这样就不会产生明明是最小生成树中的点,结果还被判重的情况了。


Code:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;

const int inf = 1 << 28;
const int maxn = 111;
int n, m, ans;
int a[maxn][maxn];
int dist[maxn];

bool prim()
{
    int mn, k, cnt;
    bool p[maxn];
    ans = 0;
    for(int i = 2; i <= n; i ++)
    {
        p[i] = false;
        dist[i] = a[1][i];
    }
    p[1] = true;
    dist[1] = 0;
    bool uni = true;
    for(int i = 1; i < n; i ++)
    {
        mn = inf;
        k = 0;
        for(int j = 1; j <= n; j ++)
        {
            if(!p[j] && dist[j] < mn)
                mn = dist[j], k = j;
        }
        p[k] = true;
        ans += mn;
        cnt = 0;
        for(int j = 1; j <= n; j ++)  // judge whether the MST is unique from previous points
            if(p[j] && a[k][j] == mn)
                cnt ++;

        if(cnt > 1)
        {
            uni = false;
            break;
        }
        for(int j = 1; j <= n; j ++)
            if(!p[j] && dist[j] > a[k][j])
                dist[j] = a[k][j];
    }
    if(!uni)
        return false;
    return true;
}

int main()
{
    int cse;
    scanf("%d", &cse);
    while(cse --)
    {
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; i ++)
            for(int j = 1; j <= n; j ++)
                a[i][j] = inf;
        while(m --)
        {
            int u, v, w;
            scanf("%d%d%d", &u, &v, &w);
            a[u][v] = a[v][u] = min(w, a[u][v]);
        }
        if(prim())
            printf("%d\n", ans);
        else
            printf("Not Unique!\n");
    }
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值