news 2026/5/1 6:46:53

信奥赛C++提高组csp-s之倍增算法思想及应用案例(4)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
信奥赛C++提高组csp-s之倍增算法思想及应用案例(4)

信奥赛C++提高组csp-s之倍增算法思想及应用案例(4)

题目描述

A 国有n nn座城市,编号从1 11n nn,城市之间有m mm条双向道路。每一条道路对车辆都有重量限制,简称限重。

现在有q qq辆货车在运输货物,司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。

输入格式

第一行有两个用一个空格隔开的整数n , m n,mn,m,表示 A 国有n nn座城市和m mm条道路。

接下来m mm行每行三个整数x , y , z x, y, zx,y,z,每两个整数之间用一个空格隔开,表示从x xx号城市到y yy号城市有一条限重为z zz的道路。
注意:x ≠ y x \neq yx=y,两座城市之间可能有多条道路。

接下来一行有一个整数q qq,表示有q qq辆货车需要运货。

接下来q qq行,每行两个整数x , y x,yx,y,之间用一个空格隔开,表示一辆货车需要从x xx城市运输货物到y yy城市,保证x ≠ y x \neq yx=y

输出格式

共有q qq行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。
如果货车不能到达目的地,输出− 1 -11

输入输出样例 #1
输入 #1
4 3 1 2 4 2 3 3 3 1 1 3 1 3 1 4 1 3
输出 #1
3 -1 3
说明/提示

对于30 % 30\%30%的数据,1 ≤ n < 1000 1 \le n < 10001n<10001 ≤ m < 10 , 000 1 \le m < 10,0001m<10,0001 ≤ q < 1000 1\le q< 10001q<1000

对于60 % 60\%60%的数据,1 ≤ n < 1000 1 \le n < 10001n<10001 ≤ m < 5 × 10 4 1 \le m < 5\times 10^41m<5×1041 ≤ q < 1000 1 \le q< 10001q<1000

对于100 % 100\%100%的数据,1 ≤ n < 10 4 1 \le n < 10^41n<1041 ≤ m < 5 × 10 4 1 \le m < 5\times 10^41m<5×104,$1 \le q< 3\times 10^4 $,0 ≤ z ≤ 10 5 0 \le z \le 10^50z105

60分代码:最大生成树 + BFS查询

#include<bits/stdc++.h>usingnamespacestd;constintN=1e4+10;// 最大节点数constintM=5e4+10;// 最大边数constintINF=0x3f3f3f3f;// 无穷大值intn,m,q;// 节点数,边数,查询数// 边结构体structnode{intx,y,z;// 边的两个端点和权重}a[M];structnode2{intto,w;};// 比较函数:按边权从大到小排序boolcmp(node a,node b){returna.z>b.z;}intfa[N];// 并查集数组// 并查集查找函数intfind(intx){if(fa[x]!=x)fa[x]=find(fa[x]);returnfa[x];}// 并查集合并函数voidunionSet(intx,inty){introotx=find(x);introoty=find(y);if(rootx==rooty)return;fa[rooty]=rootx;}vector<node2>g[N];// 最大生成树的邻接表// BFS查找两点间路径上的最小边权intbfs(ints,inte){// 如果起点和终点不在同一个连通分量,直接返回-1if(find(s)!=find(e))return-1;vector<int>minw(n+1,-1);// 记录到达每个节点的路径上的最小边权queue<int>q;minw[s]=INF;// 起点到自己的最小边权设为无穷大q.push(s);while(!q.empty()){intu=q.front();q.pop();if(u==e)returnminw[u];// 到达终点,返回结果// 遍历当前节点的所有邻居for(autox:g[u]){intv=x.to;// 邻居节点intw=x.w;// 边权if(minw[v]==-1){// 如果邻居节点还未访问过minw[v]=min(minw[u],w);// 更新到v的最小边权q.push(v);}}}return-1;}intmain(){cin>>n>>m;// 输入所有边for(inti=1;i<=m;i++){cin>>a[i].x>>a[i].y>>a[i].z;}// 按边权从大到小排序sort(a+1,a+m+1,cmp);// 初始化并查集for(inti=1;i<=n;i++)fa[i]=i;// 构建最大生成树for(inti=1;i<=m;i++){intu=a[i].x;intv=a[i].y;intw=a[i].z;if(find(u)!=find(v)){// 如果两个节点不在同一个连通分量unionSet(u,v);// 合并g[u].push_back({v,w});// 在生成树中添加边g[v].push_back({u,w});}}// 处理查询cin>>q;while(q--){intx,y;cin>>x>>y;cout<<bfs(x,y)<<endl;// 输出两点间路径上的最小边权}return0;}

60分代码:功能分析

算法思路
  1. 最大生成树构建

    • 将边按权重从大到小排序
    • 使用Kruskal算法构建最大生成树
    • 这样保证任意两点间的路径上的最小边权是最大的
  2. 查询处理

    • 对于每个查询(x, y),在最大生成树上进行BFS
    • 在BFS过程中维护从起点到当前节点的路径上的最小边权
    • 到达终点时,该值即为所求
关键特性
  • 正确性保证:最大生成树保证了任意两点间路径的最小边权是原图中所有可能路径中最大的
  • 效率优化:预处理构建生成树后,每个查询可以在O(n)时间内完成
  • 连通性检查:使用并查集快速判断两点是否连通
时间复杂度
  • 每个查询都进行一次完整的BFS,时间复杂度为O(n)
  • 查询次数q最多可达 30,000,n最多可达 10,000
  • 最坏情况复杂度:O(q × n) = 30,000 × 10,000 = 3×10⁸

满分代码

#include<bits/stdc++.h>usingnamespacestd;constintN=1e4+10;// 最大城市数constintM=5e4+10;// 最大道路数constintINF=0x3f3f3f3f;// 无穷大常量intn,m,q;// 存储原始道路信息structnode{intx,y,z;// x,y:城市编号, z:限重}a[M];// 存储生成树中的边structnode2{intto,w;// to:目标城市, w:边权(限重)};// 按限重从大到小排序,用于构建最大生成树boolcmp(node a,node b){returna.z>b.z;}// 并查集相关intfa[N];intfind(intx){if(fa[x]!=x)fa[x]=find(fa[x]);returnfa[x];}voidunionSet(intx,inty){introotx=find(x);introoty=find(y);if(rootx==rooty)return;fa[rooty]=rootx;}// LCA相关常量与数组constintLOG=15;// 对数常数,2^15=32768 > 10000intd[N];// 节点深度intf[N][LOG];// f[i][j]:节点i的2^j级祖先intminw[N][LOG];// minw[i][j]:节点i到2^j级祖先路径上的最小边权vector<node2>g[N];// 最大生成树的邻接表boolvis[N];// BFS访问标记// BFS预处理深度、父节点和最小边权voidbfs(intstart){queue<int>q;q.push(start);d[start]=1;// 根节点深度为1vis[start]=true;while(!q.empty()){intu=q.front();q.pop();// 遍历所有邻接节点for(autox:g[u]){intv=x.to;intw=x.w;if(d[v])continue;// 已访问过d[v]=d[u]+1;// 深度+1f[v][0]=u;// 父节点minw[v][0]=w;// 到父节点的边权vis[v]=true;q.push(v);}}}// 查询从s到e路径上的最大载重(最小边权的最大值)intquery(ints,inte){// 检查是否连通if(find(s)!=find(e))return-1;// 保证s的深度不小于eif(d[s]<d[e])swap(s,e);intres=INF;// 将s提升到与e同一深度,并记录路径上的最小边权for(inti=LOG-1;i>=0;i--){if(d[f[s][i]]>=d[e]){res=min(res,minw[s][i]);s=f[s][i];}}// 如果此时s==e,说明e是s的祖先if(s==e)returnres;// 同时提升s和e,直到它们的父节点相同for(inti=LOG-1;i>=0;i--){if(f[s][i]!=f[e][i]){res=min(res,min(minw[s][i],minw[e][i]));s=f[s][i];e=f[e][i];}}// 最后还要比较到LCA的两条边res=min(res,min(minw[s][0],minw[e][0]));returnres;}intmain(){cin>>n>>m;// 读入所有道路for(inti=1;i<=m;i++){cin>>a[i].x>>a[i].y>>a[i].z;}// 按限重降序排序sort(a+1,a+m+1,cmp);// 初始化并查集for(inti=1;i<=n;i++)fa[i]=i;// 构建最大生成树for(inti=1;i<=m;i++){intu=a[i].x;intv=a[i].y;intw=a[i].z;if(find(u)!=find(v)){unionSet(u,v);// 在生成树中添加边g[u].push_back({v,w});g[v].push_back({u,w});}}// 对每个连通分量进行BFS预处理for(inti=1;i<=n;i++){if(!vis[i]){bfs(i);}}// 倍增预处理:计算f和minw数组for(intk=1;k<LOG;k++){for(inti=1;i<=n;i++){f[i][k]=f[f[i][k-1]][k-1];minw[i][k]=min(minw[i][k-1],minw[f[i][k-1]][k-1]);}}// 处理查询cin>>q;while(q--){intx,y;cin>>x>>y;cout<<query(x,y)<<endl;}return0;}

满分代码:功能分析

算法思路
  1. 问题转化:将货车运输问题转化为在最大生成树上求两点间路径最小边权的问题
  2. 最大生成树:使用Kruskal算法构建最大生成树,保证任意两点间的路径具有最大的最小边权
  3. LCA优化:使用倍增法预处理树结构,实现O(logN)的路径查询
核心步骤
  1. 预处理阶段

    • 读入数据并排序
    • 构建最大生成树
    • BFS预处理深度和父节点
    • 倍增预处理祖先和路径最小值
  2. 查询阶段

    • 检查连通性
    • 通过LCA找到路径上的最小边权
时间复杂度
  • 每次查询:O(logN),N最大10000
  • 查询次数q最多 30,000
  • 最坏情况复杂度:O(q × logN)
算法优势
  • 利用最大生成树保证最优解
  • 使用LCA倍增法实现快速查询
  • 能够处理不连通的情况

更多系列知识,请查看专栏:《信奥赛C++提高组csp-s知识详解及案例实践》:
https://blog.csdn.net/weixin_66461496/category_13113932.html


各种学习资料,助力大家一站式学习和提升!!!

#include<bits/stdc++.h>usingnamespacestd;intmain(){cout<<"########## 一站式掌握信奥赛知识! ##########";cout<<"############# 冲刺信奥赛拿奖! #############";cout<<"###### 课程购买后永久学习,不受限制! ######";return0;}

1、csp信奥赛高频考点知识详解及案例实践:

CSP信奥赛C++动态规划:
https://blog.csdn.net/weixin_66461496/category_13096895.html点击跳转

CSP信奥赛C++标准模板库STL:
https://blog.csdn.net/weixin_66461496/category_13108077.html 点击跳转

信奥赛C++提高组csp-s知识详解及案例实践:
https://blog.csdn.net/weixin_66461496/category_13113932.html

2、csp信奥赛冲刺一等奖有效刷题题解:

CSP信奥赛C++初赛及复赛高频考点真题解析(持续更新):https://blog.csdn.net/weixin_66461496/category_12808781.html 点击跳转

CSP信奥赛C++一等奖通关刷题题单及题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12673810.html 点击跳转

3、GESP C++考级真题题解:

GESP(C++ 一级+二级+三级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12858102.html 点击跳转

GESP(C++ 四级+五级+六级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12869848.html 点击跳转


GESP(C++ 七级+八级)真题题解(持续更新):
https://blog.csdn.net/weixin_66461496/category_13117178.html

4、CSP信奥赛C++竞赛拿奖视频课:

https://edu.csdn.net/course/detail/40437 点击跳转

· 文末祝福 ·

#include<bits/stdc++.h>usingnamespacestd;intmain(){cout<<"跟着王老师一起学习信奥赛C++";cout<<" 成就更好的自己! ";cout<<" csp信奥赛一等奖属于你! ";return0;}
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/20 5:17:55

利用ESP32引脚实现窗帘自动控制:项目应用详解

以下是对您提供的博文内容进行 深度润色与结构优化后的技术文章 。我以一位深耕嵌入式系统多年的工程师兼教学博主身份&#xff0c;重新组织逻辑、删减冗余术语堆砌、强化工程细节、注入真实开发经验&#xff0c;并彻底去除AI生成痕迹——全文读起来像是一位在实验室调试完窗…

作者头像 李华
网站建设 2026/4/23 13:15:16

告别Whisper高延迟!SenseVoiceSmall多语言识别极速体验

告别Whisper高延迟&#xff01;SenseVoiceSmall多语言识别极速体验 还在用Whisper听一段10秒音频要等3秒&#xff1f;会议录音转文字卡在加载动画里反复刷新&#xff1f;粤语客服电话刚挂断&#xff0c;转写结果还没出来&#xff1f;不是模型不够聪明&#xff0c;而是架构拖了…

作者头像 李华
网站建设 2026/4/15 20:58:05

5分钟上手fft npainting lama:零基础实现图片重绘修复

5分钟上手fft npainting lama&#xff1a;零基础实现图片重绘修复 1. 这不是另一个“AI修图工具”&#xff0c;而是你马上能用上的图像修复方案 你有没有遇到过这些情况&#xff1a; 一张珍贵的老照片&#xff0c;角落有明显划痕和霉斑&#xff0c;想修复却不会PS电商主图里…

作者头像 李华
网站建设 2026/4/24 8:11:46

本地AI绘画自由:麦橘超然完全离线使用体验

本地AI绘画自由&#xff1a;麦橘超然完全离线使用体验 你是否试过在深夜灵光乍现&#xff0c;想立刻把脑海里的画面变成一张图&#xff0c;却卡在“pip install 失败”“CUDA 版本不匹配”“显存爆了”的循环里&#xff1f;又或者&#xff0c;你刚买了一张 RTX 4060&#xff0…

作者头像 李华
网站建设 2026/5/1 5:34:32

核心要点解析VHDL数字时钟设计的模块化思想

以下是对您提供的博文《VHDL数字时钟设计的模块化思想&#xff1a;从顶层抽象到可验证实现》进行 深度润色与工程化重构后的版本 。本次优化严格遵循您的全部要求&#xff1a; ✅ 彻底去除AI痕迹&#xff0c;语言自然、专业、有“人味”——像一位在FPGA一线带过多个工业项目…

作者头像 李华
网站建设 2026/4/23 12:51:14

PyTorch-2.x-Universal-Dev-v1.0 + matplotlib绘制模型对比图表

PyTorch-2.x-Universal-Dev-v1.0 matplotlib绘制模型对比图表 1. 为什么需要一个开箱即用的PyTorch开发环境 你有没有过这样的经历&#xff1a;花半天时间配置CUDA、PyTorch版本、matplotlib后端&#xff0c;结果发现Jupyter内核启动失败&#xff0c;或者plt.show()弹不出窗…

作者头像 李华