记录131
#include<bits/stdc++.h>
using namespace std;
#define ll long long//定义长整型别名ll,因为边权和最大可达1e14,必须用long long
const int N=1e5+10;//定义常量N,表示城市数量的最大范围
vector<pair<int,ll>>g[N];//定义邻接表g,g[u]存储与城市u相连的{相邻城市, 道路长度}
ll max_dist=0;//定义变量max_dist,用来记录首都到最远城市的距离
//定义深度优先搜索函数,u是当前城市,fa是父节点(防止走回头路),d是当前走过的距离
void dfs(int u,int fa,ll d){
max_dist=max(max_dist,d);//每次到达一个节点,都尝试更新最远距离
for(auto edge:g[u]){//遍历当前城市u的所有相邻道路
int v=edge.first;//相邻的城市编号
ll w=edge.second;//这条道路的长度
if(v!=fa){//如果相邻城市不是父节点(即不是回头路)
dfs(v,u,d+w);//继续向下深搜,距离累加当前道路长度
}
}
}
int main(){//主函数入口
ios::sync_with_stdio(false);//关闭标准流同步,提升输入输出效率
cin.tie(0);//解除cin与cout的绑定,进一步加快读取速度
int n;//变量n,表示城市的总数
ll sum=0;//定义变量sum,用来累加所有道路的长度总和
cin>>n;//读入城市的数量n
for(int i=1;i<n;i++){//循环读入n-1条道路的信息
int u,v;//定义临时变量u和v,代表一条道路连接的两个城市
ll len;//定义临时变量len,代表这条道路的长度
cin>>u>>v>>len;//读入这条道路连接的两个城市编号以及长度
g[u].push_back({v,len});//在邻接表中添加u到v的连接
g[v].push_back({u,len});//在邻接表中添加v到u的连接(因为是无向图)
sum+=len;//累加所有道路的长度
}
dfs(1,0,0);//从首都(1号城市)开始进行DFS,父节点设为0,初始距离为0
cout<<2*sum-max_dist;//输出最短总路程:所有边走两倍的代价减去最远的一条回程
return 0;//程序结束
}
题目传送门
https://www.luogu.com.cn/problem/P14076
前言
我是一名专注信奥赛(CSP-J/S、NOIP)的教练。
- 如果你觉得这篇题解对你有帮助,欢迎点击关注我的CSDN账号,我会持续更新高质量算法解析。
- 我深知算法思维的构建远比单纯通过题目更重要,本系列题解不局限于AC代码的堆砌,而是致力于拆解题目背后的逻辑链条与核心知识点
- 备赛路上若遇瓶颈,欢迎随时评论或私信,我将甄选典型疑难问题,通过视频讲解或撰写专项文章的形式,为你提供深度答疑。
核心解题思路
这道题是一道经典的树上路径贪心问题。我们需要从首都出发,遍历所有节点,并且要求总路径最短。
-
模型转化:
题目中的 n 座城市和 n-1 条双向道路,且任意两座城市均可到达,这在图论中被称为树(Tree)。首都 1 号节点就是这棵树的根节点。 -
核心贪心策略:
在树上遍历所有节点,最朴素的做法是“深度优先搜索(DFS)”:从根节点出发,走到每一个叶子节点,然后原路返回。在这个过程中,每一条边都会被经过两次(去一次,回一次)。因此,所有边的长度总和乘以 2,就是遍历所有节点并回到起点的总代价。
但是,题目有一个关键条件:“车队最后可以不返回首都”。这意味着,我们可以省去从“最远的那个城市”返回首都的那段路程。为了让总路程最小,我们省去的这段路程必须尽可能长。 -
数学公式:
最终的答案 = 所有边的长度总和 × 2 - 从首都出发能到达的最远距离。
代码分块详细解释
1. 头文件、常量与全局变量定义
#include<bits/stdc++.h>
using namespace std;
#define ll long long // 定义长整型别名 ll,因为边权和最大可达 1e14,必须用 long long
const int N=1e5+10; // 定义常量 N,表示城市数量的最大范围
vector<pair<int,ll>> g[N]; // 定义邻接表 g,g[u] 存储与城市 u 相连的 {相邻城市, 道路长度}
ll max_dist=0; // 定义变量 max_dist,用来记录首都到最远城市的距离
- 详细分析:这部分搭建了图论问题的基础数据结构。由于题目中边的长度
l_i最大可达 109,且最多有 10^5 条边,总路程可能达到 10^14 级别,远远超出int的范围,因此必须使用long long。vector<pair<int,ll>> g[N]是存储无向图(树)的标准邻接表写法,pair的第一个元素存相邻节点,第二个元素存边权。
2. 深度优先搜索(DFS)求最远距离
// 定义深度优先搜索函数,u 是当前城市,fa 是父节点(防止走回头路),d 是当前走过的距离
void dfs(int u, int fa, ll d){
max_dist = max(max_dist, d); // 每次到达一个节点,都尝试更新最远距离
for(auto edge : g[u]){ // 遍历当前城市 u 的所有相邻道路
int v = edge.first; // 相邻的城市编号
ll w = edge.second; // 这条道路的长度
if(v != fa){ // 如果相邻城市不是父节点(即不是回头路)
dfs(v, u, d + w); // 继续向下深搜,距离累加当前道路长度
}
}
}
- 详细分析:这个 DFS 函数的作用是求树的“加权直径”(从根节点出发的最长路径)。
- 防止死循环:在树中遍历必须传递父节点
fa,通过if(v != fa)确保搜索只向子节点延伸,不会在两个节点之间来回横跳。 - 距离累加:
d + w实时维护从首都(根节点)到当前节点v的总路径长度。 - 全局更新:每当 DFS 走到一个节点,就将其距离与全局变量
max_dist进行比较,最终max_dist就会保存从首都出发能到达的最远城市的距离。
- 防止死循环:在树中遍历必须传递父节点
3. 主函数:建图与边权累加
int main(){ // 主函数入口
ios::sync_with_stdio(false); // 关闭标准流同步,提升输入输出效率
cin.tie(0); // 解除 cin 与 cout 的绑定,进一步加快读取速度
int n; // 变量 n,表示城市的总数
ll sum = 0; // 定义变量 sum,用来累加所有道路的长度总和
cin >> n; // 读入城市的数量 n
for(int i = 1; i < n; i++){ // 循环读入 n-1 条道路的信息
int u, v; // 定义临时变量 u 和 v,代表一条道路连接的两个城市
ll len; // 定义临时变量 len,代表这条道路的长度
cin >> u >> v >> len; // 读入这条道路连接的两个城市编号以及长度
g[u].push_back({v, len}); // 在邻接表中添加 u 到 v 的连接
g[v].push_back({u, len}); // 在邻接表中添加 v 到 u 的连接(因为是无向图)
sum += len; // 累加所有道路的长度
}
- 详细分析:主函数的前半部分负责数据的读取和图的构建。
sum += len这一步非常关键,它直接计算出了整棵树所有边的权值之和。由于是无向图,添加边时需要双向添加(u->v和v->u都要加),保证 DFS 时能正确遍历到所有相邻节点。
4. 核心计算与输出
dfs(1, 0, 0); // 从首都(1号城市)开始进行 DFS,父节点设为 0,初始距离为 0
cout << 2 * sum - max_dist; // 输出最短总路程:所有边走两倍的代价减去最远的一条回程
return 0; // 程序结束
}
- 详细分析:这是整个算法的收尾。调用
dfs(1, 0, 0)从根节点开始遍历,求出最远回程距离max_dist。最后,利用贪心公式2 * sum - max_dist直接得出答案。例如在样例 1 中,总边权和为 6+1+5=12,两倍是 24;从首都 1 出发最远能走到 4 号城市(距离为 1+5=6),所以最终答案是 24 - 6 = 18。
核心逻辑总结表
| 代码模块 | 核心变量/操作 | 精炼作用 | 解决的痛点 |
|---|---|---|---|
| 数据类型 | #define ll long long | 使用 64 位长整型 | 防止 105 条边、每条边 109 权值累加时发生数据溢出 |
| 图存储 | vector<pair<int,ll>> g[N] | 邻接表存无向图 | 完美适配 n ≤ 105 的稀疏图,比邻接矩阵更节省空间 |
| DFS 防回头 | if(v != fa) | 记录父节点并剪枝 | 避免在树的双向边中陷入无限递归死循环 |
| 贪心公式 | 2 * sum - max_dist | 核心数学推导 | 将复杂的“遍历所有节点的最短路径”问题,转化为简单的“总边权*2 - 最长单程” |
| IO 优化 | ios::sync_with_stdio(false) | 提升输入输出效率 | 解决 105 级别数据量下 cin/cout 可能导致的超时问题 |
525

被折叠的 条评论
为什么被折叠?



