线上OJ:
核心思想:
本题本质是最小生成树,可以采用 Prim 和 Kruskal 算法来解。
解法一、Prim
Prim 算法:蓝白点
阶段0:初始化(minv[], vis[], minv[1])
阶段1:在剩余蓝点中找出 minv 最小的点 k
阶段2:由于k点是新加入的白点,所以要更新剩余蓝点到白点的 minv,以备下一轮使用
#include <bits/stdc++.h>
using namespace std;
const int N = 305;
int n, m, e[N][N], minv[N], maxv = -1; // minv[i] 存储节点i与白点相连的最小权值; e[i][j]存放i->j的边权
bool vis[N]; // vis[i] = true 表示i已经访问并加入最小生成树(true说明已经变成白点,false说明还是蓝点)
int main()
{
scanf("%d %d", &n, &m);
for(int i = 1; i <= m; i++) // 读入条边
{
int x, y, v;
scanf("%d %d %d", &x, &y, &v);
e[x][y] = e[y][x] = v;
}
memset(minv, 0x3f, sizeof(minv));
memset(vis, 0, sizeof(vis));
minv[1] = 0; // Prim算法以1为起点生成最小生成树,所以1到自己的最小权值为0
// Prim 算法主体
for(int i = 1; i <= n; i++) // 外循环 n 次(n个点)
{
int k = 0;
// 阶段一:在剩余蓝点中找出 minv 最小的点
for(int j = 1; j <= n; j++)
if( !vis[j] && (minv[j] < minv[k]) ) k = j;
vis[k] = true; // 转为白点
// 阶段二:由于k点是新的白点,所以要更新剩余蓝点到白点的 minv
for(int j = 1; j <= n; j++)
if( !vis[j] && (e[k][j] != 0) && (e[k][j] < minv[j]) )
minv[j] = e[k][j];
}
for(int i = 1; i <= n; i++) maxv = max(maxv, minv[i]);
printf("%d %d\n", n - 1, maxv);
return 0;
}
解法二、 Kruskal
Kruskal 算法:每一轮找边权最小的,且端点根节点不同的,将其合并(并查集)
step1、根据边权v,对边进行升序排序
step2、for循环边,如果边的两个端点分属不同阵营,则合并
step3、合并一次就是找到一条边,当找到 n-1 条边时,结束循环
#include <bits/stdc++.h>
using namespace std;
const int N = 305;
struct Node
{
int x, y, v; // Kruskal可以使用结构体存储边的两端坐标x和y,以及边权v。因为要根据边权v进行排序,并合并边的两个端点x和y
};
Node e[90010];
int n, m, p[N], maxv = -1; // Kruskal使用的是并查集思想,所以需要p[i] 存储i的根节点;
bool cmp(Node a, Node b)
{
return a.v < b.v;
}
int find(int x)
{
if(p[x] != x) p[x] = find(p[x]);
return p[x];
}
int main()
{
scanf("%d %d", &n, &m);
for(int i = 1; i <= m; i++) // 读入条边
{
int x, y, v;
scanf("%d %d %d", &x, &y, &v);
e[i] = {x, y, v};
}
for(int i = 1; i <= n; i++) p[i] = i; // 初始化每个元素的根节点为自己(即每个元素都是独立的)
// Kruskal核心代码
sort(e + 1, e + 1 + m, cmp); // 根据边权v,进行升序排序
int k = 0;
for(int i = 1; i <= m; i++) // 总共有m条边,已完成顺序,寻找第一个两边端点分属不同集合的边,将他们合并
{
int a = find(e[i].x), b = find(e[i].y); // 找到边的两个端点的根节点
if( a != b ) // 如果边的两个端点的根节点在不同的阵营
{
p[a] = b; // 则合并
maxv = max(maxv, e[i].v);
k++;
if( k == n - 1 ) break;
}
}
printf("%d %d\n", n - 1, maxv);
return 0;
}