P14919 [GESP202512 六级] 路径覆盖

题解:洛谷 P14919 [GESP202512 六级] 路径覆盖 等知名刷题平台精心挑选而来,并结合各平台提供的算法标签和难度等级进行了系统分类。题目涵盖了从基础到进阶的多种算法和数据结构,旨在为不同阶段的编程学习者提供一条清晰、平稳的学习提升路径。一行,一个整数,表示在满足所有叶子到根的路径上至少有一个黑色结点的前提下,染色代价之和的最小值。中的若干个结点染为黑色,使得所有叶子到根的路径上至少有一个黑色结点。,你需要在满足以上条件的情况下,最小化染色代价之和。的父结点的编号,保证。本文分享的必刷题目是从。中没有子结点的结点。染为黑色所需的代价。 阅读详情

记录134

#include<bits/stdc++.h> // 引入C++万能头文件,包含vector等常用标准库
using namespace std; // 使用标准命名空间std
#define ll long long // 定义长整型别名ll,因为代价c[i]最大为1e9,累加容易溢出int
const int N = 1e5 + 10; // 定义常量N,表示结点数量的最大范围
int n; // 定义全局变量n,表示结点的总数
vector<int> g[N]; // 定义邻接表g,g[u]存储结点u的所有子结点
ll c[N]; // 定义数组c,c[i]表示将结点i染为黑色所需的代价
ll dp[N]; // 定义dp数组,dp[i]表示以i为根的子树满足条件的最小代价

// 定义深度优先搜索函数,u表示当前正在处理的结点
void dfs(int u){
    ll sum=0; // 定义变量sum,用来累加当前结点所有子结点的dp值
    bool is_leaf=true; // 定义布尔变量is_leaf,标记当前结点是否为叶子结点
    for(int v:g[u]){ // 遍历当前结点u的所有子结点v
        is_leaf=false; // 只要有子结点,就说明u不是叶子结点
        dfs(v); // 递归处理子结点v,自底向上计算子结点的dp值
        sum+=dp[v]; // 将子结点v的最小代价累加到sum中
    }
    if(is_leaf){ // 如果当前结点u是叶子结点
        dp[u]=c[u]; // 叶子结点必须自己染色,否则它到自己的路径就没有黑点
    } else { // 如果当前结点u不是叶子结点
        // 状态转移:取“自己染黑”和“子结点们自己解决”两者中的较小值
        dp[u]=min(c[u], sum); 
    }
}

int main(){ // 主函数入口
    ios::sync_with_stdio(false); // 关闭标准流同步,提升输入输出效率
    cin.tie(0); // 解除cin与cout的绑定,加快读取速度
    cin>>n; // 读入结点的数量n
    // 循环读入n-1个父结点信息,并建立邻接表
    for(int i=2;i<=n;i++){
        int f; // 定义临时变量f,表示结点i的父结点
        cin>>f; // 读入结点i的父结点编号
        g[f].push_back(i); // 在邻接表中将i加入f的子结点列表
    }
    // 循环读入n个结点的染色代价
    for(int i=1;i<=n;i++){
        cin>>c[i]; // 读入结点i的染色代价
    }
    dfs(1); // 从根结点1开始进行DFS,计算整棵树的最小代价
    cout<<dp[1]<<"\n"; // 输出以根结点1为根的子树的最小代价,即最终答案
    return 0; // 程序正常结束
}

题目传送门https://www.luogu.com.cn/problem/P14919


前言

我是一名专注信奥赛(CSP-J/S、NOIP)的教练。

  • 如果你觉得这篇题解对你有帮助,欢迎点击关注我的优快云账号,我会持续更新高质量算法解析。
  • 我深知算法思维的构建远比单纯通过题目更重要,本系列题解不局限于AC代码的堆砌,而是致力于拆解题目背后的逻辑链条与核心知识点
  • 备赛路上若遇瓶颈,欢迎随时评论或私信,我将甄选典型疑难问题,通过视频讲解或撰写专项文章的形式,为你提供深度答疑。

 核心解题思路

这道题是一道经典的树形动态规划(Tree DP)问题。

  1. 问题转化:
    题目要求所有叶子结点到根结点的路径上至少有一个黑色结点。我们可以将这个问题分解到每一棵子树中去解决。对于任意一个结点 u,要满足它子树内所有叶子到根的路径合法,只有两种最优的决策方案:

    • 方案一:结点 u 自己染成黑色。如果 u 是黑色的,那么从 u 的子树中任意叶子向上走到 u 时,路径上就已经有了黑色结点。此时,u 的子结点们无需再为了“向上覆盖”而承担额外代价,它们只需要各自管好自己的子树即可。
    • 方案二:结点 u 保持白色。如果 u 不染黑,那么为了覆盖 u 的子树中所有叶子的路径,u 的所有子结点 v 必须各自独立地满足条件(即 v 的子树内的叶子到 v 的路径必须有黑点)。
  2. 状态定义与转移:
    定义 dp[u] 为:使得以 u 为根的子树中,所有叶子到 u 的路径上至少有一个黑色结点的最小代价。

    • 如果 u 是叶子结点:它必须自己染黑,dp[u] = c[u]。
    • 如果 u 不是叶子结点:取上述两种方案的最小值,即 dp[u] = min(c[u], sum(dp[v])),其中 sum(dp[v]) 是 u 所有子结点 v 的 dp 值之和。

 代码分块详细解释

1. 头文件、常量与全局变量定义

#include<bits/stdc++.h> 
using namespace std; 
#define ll long long // 定义长整型别名 ll,因为代价 c[i] 最大为 1e9,累加容易溢出 int
const int N = 1e5 + 10; // 定义常量 N,表示结点数量的最大范围
int n; // 定义全局变量 n,表示结点的总数
vector<int> g[N]; // 定义邻接表 g,g[u] 存储结点 u 的所有子结点
ll c[N]; // 定义数组 c,c[i] 表示将结点 i 染为黑色所需的代价
ll dp[N]; // 定义 dp 数组,dp[i] 表示以 i 为根的子树满足条件的最小代价
  • 详细分析:由于染色代价 c_i 最大可达 10^9,在极端情况下累加可能会超出 int 的范围,因此 c 数组和 dp 数组必须使用 long long 类型。vector<int> g[N] 用于存储树的子结点关系,因为题目保证了父结点编号小于子结点(f_i < i),这实际上已经给出了一种拓扑序,但使用 DFS 递归处理更加直观。

2. 深度优先搜索(DFS)与状态转移

// 定义深度优先搜索函数,u 表示当前正在处理的结点
void dfs(int u){
    ll sum = 0; // 定义变量 sum,用来累加当前结点所有子结点的 dp 值
    bool is_leaf = true; // 定义布尔变量 is_leaf,标记当前结点是否为叶子结点
    for(int v : g[u]){ // 遍历当前结点 u 的所有子结点 v
        is_leaf = false; // 只要有子结点,就说明 u 不是叶子结点
        dfs(v); // 递归处理子结点 v,自底向上计算子结点的 dp 值
        sum += dp[v]; // 将子结点 v 的最小代价累加到 sum 中
    }
    if(is_leaf){ // 如果当前结点 u 是叶子结点
        dp[u] = c[u]; // 叶子结点必须自己染色,否则它到自己的路径就没有黑点
    } else { // 如果当前结点 u 不是叶子结点
        // 状态转移:取“自己染黑”和“子结点们自己解决”两者中的较小值
        dp[u] = min(c[u], sum); 
    }
}
  • 详细分析:这是树形 DP 的核心。
    • 自底向上计算:通过 dfs(v) 先递归处理完所有子结点,拿到子结点的最优解 dp[v]。
    • 叶子结点处理:如果 u 是叶子,它没有任何子结点可以分担“覆盖路径”的责任,所以 dp[u] 只能等于它自身的染色代价 c[u]。
    • 非叶子结点贪心:如果 u 不是叶子,我们面临抉择。要么花费 c[u] 染黑自己,一劳永逸地覆盖所有子树路径;要么自己不染,把压力下放,让所有子结点各自承担自己的子树代价(即 sum)。取两者较小值即为 dp[u] 的最优解。

3. 主函数:建图与初始化

int main(){ 
    ios::sync_with_stdio(false); // 关闭标准流同步,提升输入输出效率
    cin.tie(0); // 解除 cin 与 cout 的绑定,加快读取速度
    cin >> n; // 读入结点的数量 n
    // 循环读入 n-1 个父结点信息,并建立邻接表
    for(int i = 2; i <= n; i++){
        int f; // 定义临时变量 f,表示结点 i 的父结点
        cin >> f; // 读入结点 i 的父结点编号
        g[f].push_back(i); // 在邻接表中将 i 加入 f 的子结点列表
    }
    // 循环读入 n 个结点的染色代价
    for(int i = 1; i <= n; i++){
        cin >> c[i]; // 读入结点 i 的染色代价
    }
  • 详细分析:题目给出的输入是 f_2, f_3, ..., f_n,即每个结点的父结点。我们在读入时直接将其转化为子结点列表 g[f].push_back(i),从而构建出一棵以 1 为根的有向树。

4. 启动 DP 与输出结果

    dfs(1); // 从根结点 1 开始进行 DFS,计算整棵树的最小代价
    cout << dp[1] << "\n"; // 输出以根结点 1 为根的子树的最小代价,即最终答案
    return 0; // 程序正常结束
}
  • 详细分析:调用 dfs(1) 从整棵树的根结点开始递归。当递归返回时,dp[1] 中存储的就是使得整棵树(以 1 为根)所有叶子到根的路径上都有黑色结点的最小总代价。

核心逻辑总结表

代码模块核心变量/操作精炼作用解决的痛点
数据类型#define ll long long使用 64 位长整型防止 105 个结点、每个代价 10^9 累加时发生数据溢出
树的存储g[f].push_back(i)建立父到子的有向边将输入的父结点数组转化为便于 DFS 遍历的邻接表
叶子判断bool is_leaf标记当前结点是否有子结点区分基础状态(叶子必须染)和递推状态(非叶子做抉择)
状态转移dp[u] = min(c[u], sum)树形 DP 核心贪心公式在“自己承担代价”与“下放代价给子结点”之间做出最优选择
自底向上dfs(v); sum += dp[v]先递归子结点再合并确保在计算父结点最优解时,所有子结点的最优解已经计算完毕
STM32F407 HAL库定时器触发ADC采集与DMA数据传输(定时器TIM+ADC+DMA) 在STM32F407系列微控制器的开发中,结合定时器、ADC(模数转换器)与DMA(直接存储器访问)控制器,能够显著提升数据采集与传输的效率。本文将指导你如何使用STM32 HAL库,通过定时器触发ADC1的单通道采集,利用DMA传输数据,最终通过串口将电压值打印出来。具体实现中,我们将读取ADC1的通道5(对应引脚PA5),并将转换得到的电压值发送到串口助手上进行显示。 阅读详情

相关推荐

避开新手第一个坑:手把手教你正确安装Perforce服务器与客户端(P4d/P4V)

本文详细指导如何正确安装和配置Perforce服务器与客户端(P4d/P4V),避免新手常见的安装顺序错误。从环境准备到分步安装,再到常见问题排查和性能优化,帮助开发者快速搭建稳定的版本控制环境,提升团队协作效率。

weixin_26804345的博客 668

洛谷 P14919:[GESP202512 六级] 路径覆盖 ← 树形DP

动态规划的状态定义:f[i] 表示以节点 i 为根的子树满足“所有叶子到 i 的路径至少有一个黑色结点”的最小染色代价。

hnjzsyjyj的专栏 419

康耐视 VisionPro CogImageFileTool(将获得的图像保存至文件或从文件中获取图像)工具

VisionPro CogImageFileTool(将获得的图像保存至文件或从文件中获取图像)工具

zhihuishuxia__的博客 2065

洛谷P14919 [GESP202512 六级] 路径覆盖

这道题要求在一棵有根树中,用最小的代价将某些节点染黑,使得每个叶子到根的路径上至少有一个黑色节点。对于每个节点x,有两种选择:要么直接染黑x(代价为c[x]),要么依靠子节点的染色结果(代价是子节点dp值之和)。叶子节点的dp值就等于其染色代价,非叶子节点则比较自身代价和子节点代价的总和。一行,一个整数,表示在满足所有叶子到根的路径上至少有一个黑色结点的前提下,染色代价之和的最小值。中的若干个结点染为黑色,使得所有叶子到根的路径上至少有一个黑色结点。,你需要在满足以上条件的情况下,最小化染色代价之和。

aierxiang的博客 691

Ubuntu Rtx3090 Cuda11.7 安装 pytorch3d

pytorch ubuntu pytorch3d 安装

G果的博客 2273

uni-app:实现APP设置手机闹钟+取消闹钟+响铃重复/文字等,帮用户设置好闹钟并备注文本,uni-app设置安卓系统闹钟和苹果ios系统闹钟(uniapp调用手机上的闹钟,详细示例代码demo)

uniapp设置安卓和iOS闹钟的插件,uni-app设置安卓系统闹钟,uniapp设置苹果ios系统闹钟,uniapp设置定时闹铃的完整解决方案,UniApp设置手机闹钟,uniApp如何设置手机闹铃,uniapp设置闹钟,设置闹钟提醒,uni-app设置安卓系统闹钟_通过uniapp设置原声闹钟,调用android系统闹钟,如何在App中启动系统闹钟,uniapp如何取消闹钟,通过调用ios的原生接口来设置手机闹钟,uni-app 调用手机闹钟功能,uniap调用ios系统闹钟,uniapp调用手机上的

✅ 精选推荐文章 4235

《P14919 [GESP202512 六级] 路径覆盖》

这是一道关于树形结构的最小代价染色问题。题目要求在有根树中选择若干节点染黑,使得每个叶子到根的路径上至少有一个黑节点,同时最小化染色总代价。输入包括节点数n、父节点关系和染色代价,输出满足条件的最小总代价。解题思路可以采用动态规划或贪心算法,自底向上处理每个子树的最小代价。对于每个节点,可以选择染黑(代价ci)或继承子节点最优解之和。时间复杂度需控制在O(n)级别以处理1e5规模的数据。样例1的最小代价为2(染节点2),样例2的最小代价为10。

Jasmine_llq的博客 153

VHDL六层电梯控制器及仿真

六层电梯电梯 设计要求:设计一个六层电梯控制系统要求有超载、关门中断、提前关门清除报警信电梯外人的上升请求信号,电梯外人的下降请求信号, 电梯外人上升请求信号显示电梯外人下降请求信号显示到达楼层信号 设计思路:输入有clk时钟信号(频率为2Hz) full,deng,quick,clr超载、关门中断、提前关门清除报警c_u1,c_u2,c_u3,c_u4,c_u5电梯外人的上升请求c_d2,c_d3,c_d4,c_d5,c_d6 电梯外人的下降请求 d1,d2,d3,d4,d5,d6电梯内人的...

QQ_778132974的博客 1709

洛谷 P14919:[GESP202512 六级] 路径覆盖 ← DFS + 树形DP

这段代码通过树形 DP 求解树上最小覆盖代价,采用后序 DFS 遍历实现:以 f[u] 表示以结点 u 为根的子树完全覆盖的最小花费,递归处理每个结点的所有子结点并累加子树最优解得到总和,随后对每个结点做出两种最优决策 ——直接将自身染色覆盖整棵子树,或不染色、依赖所有子结点的子树自行覆盖,取两种方案的最小花费更新 f[u],最终根结点的 f[1] 即为整棵树的最小覆盖总代价。

hnjzsyjyj的专栏 977

携程机票数据爬取实战:使用 Python 获取携程网机票信息

通过本篇博客,我们详细介绍了如何使用 Python 爬取携程网的机票信息,涉及了静态网页爬取(使用 Requests 和 BeautifulSoup)和动态网页爬取(使用 Selenium)两种方法。通过获取的数据,我们不仅能够进行票价分析,还可以构建更为复杂的预测模型或趋势分析工具。爬虫技术不仅能够帮助我们收集有价值的数据,还能够为商业决策提供数据支持。在实际应用中,我们可以结合更多的分析方法,如自然语言处理、图像处理等,进一步提升数据的价值。

2201_76125261的博客 1658

你的小程序地图功能还只是“摆设”吗?支付宝地图API的深度玩法都在这里了!

🏆本文收录于《滚雪球学支付宝小程序开发》专栏,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!

**My Coding Family** 1823

CCF-GESP计算机学会等级考试2025年12月六级C++T1 路径覆盖

题目要求在一棵有根树中选择若干节点染黑,使得所有叶子到根的路径上至少有一个黑点,且总染色代价最小。通过树形动态规划解决:定义dp[i]为以i为根的子树的最小覆盖代价。从叶子到根逆序遍历节点,对每个节点i,取自己染黑的代价c[i]或子节点代价和的最小值作为dp[i],最后根节点的dp值即为答案。算法时间复杂度O(n),空间复杂度O(n),高效处理了n≤1e5的数据规模。

长春高老师编程 1062

020、Sensor 供电设计:AVDD、DVDD、IOVDD 的上电时序与纹波要求

本文总结了图像传感器(Sensor)供电设计中的关键要点与常见问题:1. 三路供电特性差异:AVDD(模拟供电)对纹波最敏感(通常<30mVpp),DVDD需应对大电流瞬态变化,IOVDD需满足MIPI接口电压稳定要求。2. 上电时序至关重要,应遵循"模拟→数字→IO"的启动顺序,时序偏差可能导致初始化失败。3. 纹波控制需合理选择电容组合(如10μF+0.1μF),注意布局走线及电容ESR/ESL特性。4. 调试建议使用示波器多通道测量时序和纹波,重点关注传感器启动瞬间的电源特性。5. 经验教训包括预留设

sundong_81的博客 529

GESP认证C++编程真题解析 | 202512 六级

下面的函数能正确判断一棵树是不是二叉排序树(左边的数字要比当前数字小,右边的数字要比当前数字大)。虚函数的核心作用是实现运行时多态(动态绑定),即根据实际对象类型调用对应函数,A正确。在二叉排序树(BST)中,若某结点的左子树为空,则该结点一定是整棵树中的最小值结点。面向对象编程中,封装是指将数据和操作数据的方法绑定在一起,并对外隐藏实现细节。递推和递归都可以实现动态规划,在实现良好的情况下,两种方式的时间复杂度相当。递归处理左子树,递归处理右子树,最后输出根结点的信息,这种为后序遍历。

团爸讲算法 598

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

记——那次难忘的GESP六级考试(202603)+编程题题解

本文记录了作者参加GESP六级考试的全过程,重点描述了备考和考试当天的经历。作者详细列出了自己完成的13道编程题目及解题思路,包括DP、贪心、树状数组等算法题,同时也提到未完成的8道题目。考试当天经历了忘带鼠标的意外,利用触控板继续练习。文章还提到选择题涉及动态规划、树遍历等基础知识,整体展现了作者从紧张备考到从容应考的心路历程,以及面对突发状况的应对能力。

2401_84010520的博客 641

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

图论路径覆盖算法解析与竞赛应用

路径覆盖是图论中的经典问题,指用不相交路径覆盖有向无环图所有顶点。其核心原理是通过Konig定理转化为二分图匹配问题,利用匈牙利算法求解。该算法在时间复杂度O(VE)内可求得最小路径数,通过优化可达O(√VE)。路径覆盖技术具有重要工程价值,广泛应用于任务调度、网络路由优化和自动化测试等场景。以GESP六级考题P14919为例,算法实现需注意DAG特性处理、顶点拆分技巧以及匈牙利算法的DFS实现细节,这些知识点对编程竞赛和实际软件开发都至关重要。

weixin_33290540的博客 183

视频隐私脱敏工程化:CSRT/KCF追踪算法在本地推理中的实现与边界

在短视频内容合规审查中,动态遮挡(Dynamic Masking)是高频且高耗时的环节。传统手动关键帧打码不仅效率低下,且在目标快速移动或遮挡时极易出现遮罩漂移。本文从计算机视觉工程角度,解析基于CSRT与KCF算法的目标追踪方案在视频隐私处理中的技术路径、性能权衡及落地实践。

Mysssone的博客 297

把 KV Cache 压到 FP4:BitDecoding-FP4 的算法-系统协同设计复盘

长上下文推理卡在KV Cache的显存与带宽上,而现有FP4方案要么精度崩、要么速度废。这篇文章复盘BitDecoding-FP4如何用三个算法补丁加一个系统重构,在B300上把服务吞吐提升2.4–3.0倍,精度几乎不掉。

helloagent的博客 308

【算法拾光】链表 哈希表|K个一组翻转链表 + 两数之和

K个一组翻转链表需分段反转,借助哑节点与前驱指针维护连接,避免断链;两数之和利用哈希表实现O(1)查找,空间换时间,高效定位目标组合。前者考察链表指针操作,后者聚焦映射关系构建,均为面试高频题。

wuqingyigexiaoba的博客 328

排序算法深度笔记(下):非比较类排序的线性突破与工程边界

排序算法时间复杂度额外空间复杂度稳定性是否基于比较选择排序O(N^2)(O(1))不稳定是冒泡排序O(N^2)(O(1))稳定是插入排序O(N^2)(O(1))稳定是归并排序O(N\log N)(O(N))稳定是快速排序O(N\log N)O(log N)不稳定是堆排序O(N\log N)O(1)不稳定是计数排序(O(N + M))O(M))稳定否基数排序O(N)稳定否桶排序平均O(N) / 最坏O(N^2)

2401_87069538的博客 601

优选算法:分治-归并排序

交易逆序对的总数

Wangshijie1015的博客 144

算法日常・每日刷题--<动态规划>42

本题为“不相交的线”,与最长公共子序列问题相似。使用动态规划求解:定义 dp[i][j] 表示 nums1[0:i] 与 nums2[0:j] 的最大不相交连线数。若元素相等,则继承左上值加1;否则取上方或左方最大值。最终返回 dp[m][n],即两数组的最大不相交连线数。时间复杂度 O(mn),空间复杂度 O(mn)。

2503_92261635的博客 23

408 算法 06|堆与 Top-K:最小的 10 个数,到底该用数组还是堆

2022(42) 找 100 万个数里最小的 10 个,官方给了"10 元素有序数组"和"大根堆"两种答案。我把两种都实现并数了比较次数:k=10 时两者几乎打平,k=10000 时堆少 111 倍。另附 2021(42) 计数排序的比较次数与稳定性实测。

m0_56226131的博客 418

408 算法 04|主元素与三元组最小距离:两种“扫描时做决定“的思路

2013(41) 的主元素用摩尔投票一遍选出候选,但真正的考点是"选完必须验票";2020(41) 的三元组最小距离,三指针每次只挪最小的那个,靠的是 D=2×(max−min) 这个恒等式。两套完整 C 代码加真实输出,附官方评分档位表。

m0_56226131的博客 417

牛客周赛165C题(前后缀性质,递推,排序,枚举)

题目大意:给定长度为 `n` 的数组,选择 `k ∈ [1,n]`,将前 `k` 个元素整体翻转,后面的元素不变。要求操作后的整个数组非递减,统计满足条件的 `k` 的数量。题目链接:https://ac.nowcoder.com/acm/contest/141721/C。时间复杂度:O(nlogn)

2501_94316951的博客 173
上一篇: P15800 [GESP202603 六级] 选数
下一篇: P15801 [GESP202603 六级] 完全二叉树
谢天谢地AC了 | 信奥教练
博客等级 码龄1年 53粉丝 177原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值