题意很简单:给你一些点和边的关系,求最小生成树是否是唯一的,若不唯一,则输出最小生成树的长度。
解题思路:在每次找到一条边之后,看这个新找到的点是否只从一个已经找过的点延伸过来,如果能找到两个以上的点,那么说明最小生成树不止一个,退出判断即可。
但是我一开始在做这道题的时候,思路却是:找到一个新的点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");
}
}