题目描述:
7-14 最小生成树的唯一性(30 分)
给定一个带权无向图,如果是连通图,则至少存在一棵最小生成树,有时最小生成树并不唯一。本题就要求你计算最小生成树的总权重,并且判断其是否唯一。
输入格式:
首先第一行给出两个整数:无向图中顶点数 N(≤500)和边数 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;
}