题目 3146: 蓝桥杯2023年第十四届省赛真题-网络稳定性 时间限制: 1.5s 内存限制: 256MB

有一个局域网,由 n 个设备和 m 条物理连接组成,第 i 条连接的稳定性为wi 。

对于从设备 A 到设备 B 的一条经过了若干个物理连接的路径,我们记这条路径的稳定性为其经过所有连接中稳定性最低的那个。

我们记设备 A 到设备 B 之间通信的稳定性为 A 至 B 的所有可行路径的稳定性中最高的那一条。

给定局域网中的设备的物理连接情况,求出若干组设备 xi 和 yi 之间的通信稳定性。如果两台设备之间不存在任何路径,请输出 −1 。

输入格式

输入的第一行包含三个整数 n, m, q ,分别表示设备数、物理连接数和询问数。

接下来 m 行,每行包含三个整数 ui , vi ,wi ,分别表示 ui 和 vi 之间有一条稳定性为 wi 的物理连接。

接下来 q 行,每行包含两个整数 xi , yi ,表示查询 xi 和 yi 之间的通信稳定性。

输出格式

输出 q 行,每行包含一个整数依次表示每个询问的答案。

样例输入

复制代码
5 4 3
1 2 5
2 3 6
3 4 1
1 4 3
1 5
2 4
1 3

样例输出

复制代码
-1
3
5

提示

对于 30% 的评测用例,n, q ≤ 500,m ≤ 1000 ;

对于 60% 的评测用例,n, q ≤ 5000,m ≤ 10000 ;

对于所有评测用例,2 ≤ n, q ≤ 1e5,1 ≤ m ≤ 3 × 1e5,1 ≤ ui , vi , xi , yi ≤ n,

1 ≤ wi ≤ 1e6,ui ≠ vi,xi ≠ yi 。

这道题其实是一个经典的最大生成树 + 最近公共祖先 (LCA) 问题。我们来拆解一下逻辑:

1. 核心思路:最大生成树

题目要求路径上"最小权值的最大值"。

想象一下,如果你想从 A 到 B 走一条尽可能"稳"的路,你肯定优先选权值大的边。当你按照边权从大到小去建立一棵树(直到 A 和 B 连通)时,这条树上路径的最小边权,就是你要的答案。

  • 结论 :任意两点间的"最大稳定性路径"一定在最大生成树上。

  • 做法:使用 Kruskal 算法(把边权排序后反过来取),构建一棵最大生成树。

2. 树上查询:LCA 与 路径最小值

在一棵树上,两点 u, v 之间的唯一路径经过它们的 LCA

我们需要求出这条路径上权值最小的边。我们可以利用倍增法 在计算 LCA 的同时,维护一个 min_w[u][i] 数组,表示从 u 向上跳 2^i 层这段路径上的最小边权。


3. C++ 实现步骤

第一步:Kruskal 构建最大生成树

使用并查集保证不形成环,优先连权重大的边。

第二步:DFS 预处理

求出深度 deep[],以及倍增数组 fa[u][i](祖先)和 min_v[u][i](路径最小值)。

第三步:倍增 LCA 查询

跳跃过程中不断更新最小值。

#include <iostream>

#include <vector>

#include <algorithm>

using namespace std;

const int MAXN = 100005;

const int LOGN = 18;

const int INF = 1e9;

struct Edge {

int u, v, w;

};

bool compareEdges(Edge a, Edge b) {

return a.w > b.w; // 最大生成树,按权值降序

}

struct Node {

int to, w;

};

vector<Node> adj[MAXN];

int fa[MAXN][LOGN + 1], min_v[MAXN][LOGN + 1];

int depth[MAXN], p[MAXN];

// 并查集

int find(int x) {

if (p[x] == x) return x;

return p[x] = find(p[x]);

}

void dfs(int u, int father, int w, int d) {

depth[u] = d;

fa[u][0] = father;

min_v[u][0] = w;

for (int i = 1; i <= LOGN; i++) {

fa[u][i] = fa[fa[u][i - 1]][i - 1];

min_v[u][i] = min(min_v[u][i - 1], min_v[fa[u][i - 1]][i - 1]);

}

for (auto &edge : adj[u]) {

if (edge.to != father) {

dfs(edge.to, u, edge.w, d + 1);

}

}

}

int query(int u, int v) {

if (find(u) != find(v)) return -1; // 不连通

int res = INF;

if (depth[u] < depth[v]) swap(u, v);

// 先跳到同一深度

for (int i = LOGN; i >= 0; i--) {

if (depth[fa[u][i]] >= depth[v]) {

res = min(res, min_v[u][i]);

u = fa[u][i];

}

}

if (u == v) return res;

// 一起向上跳

for (int i = LOGN; i >= 0; i--) {

if (fa[u][i] != fa[v][i]) {

res = min(res, min(min_v[u][i], min_v[v][i]));

u = fa[u][i];

v = fa[v][i];

}

}

return min(res, min(min_v[u][0], min_v[v][0]));

}

int main() {

ios::sync_with_stdio(false);

cin.tie(0);

int n, m, q;

cin >> n >> m >> q;

vector<Edge> edges(m);

for (int i = 0; i < m; i++) cin >> edges[i].u >> edges[i].v >> edges[i].w;

sort(edges.begin(), edges.end(), compareEdges);

for (int i = 1; i <= n; i++) p[i] = i;

int count = 0;

for (auto &e : edges) {

int rootU = find(e.u), rootV = find(e.v);

if (rootU != rootV) {

p[rootU] = rootV;

adj[e.u].push_back({e.v, e.w});

adj[e.v].push_back({e.u, e.w});

if (++count == n - 1) break;

}

}

// 处理森林情况(可能不连通)

for (int i = 1; i <= n; i++) {

if (!depth[i]) dfs(i, 0, INF, 1);

}

while (q--) {

int x, y;

cin >> x >> y;

cout << query(x, y) << "\n";

}

return 0;

}

相关推荐
七禾页丫3 小时前
面试记录19 软件设计师
面试·职场和发展
無限進步D4 小时前
简单贪心算法 cpp
c++·算法·贪心算法·蓝桥杯·入门·竞赛
爱吃涮毛肚的肥肥(暂时吃不了版)4 小时前
Leetcode——181.超过经理收入的员工
算法·leetcode·职场和发展
醉颜凉4 小时前
Seal^_^【送书活动第8期】——《ChatGLM3大模型本地化部署、应用开发与微调》
人工智能·职场和发展·送书活动·chatglm3大模型
仰泳的熊猫5 小时前
题目2580:蓝桥杯2020年第十一届省赛真题-分类计数
数据结构·c++·算法·蓝桥杯
软件测试君5 小时前
自动化测试路线图之自动化测试完整指南
自动化测试·软件测试·测试工具·面试·职场和发展·单元测试·职场经验
_饭团5 小时前
C 语言数据存储全解析:原反补码、大小端与 IEEE 754 浮点数
c语言·数据结构·算法·leetcode·面试·蓝桥杯·学习方法
中小企业实战军师刘孙亮6 小时前
农贸批发市场招商难?从卖摊位变经营赋能破局-佛山鼎策创局破局增长咨询
职场和发展·新媒体运营·创业创新·需求分析·内容运营
酉鬼女又兒6 小时前
零基础快速入门前端JavaScript 浏览器环境输入输出语句全解析:从弹框交互到控制台调试(可用于备赛蓝桥杯Web应用开发赛道)
前端·javascript·职场和发展·蓝桥杯·js