【BFS 动态规划】P12382 [蓝桥杯 2023 省 Python B] 树上选点|普及+

【BFS 动态规划】P12382 [蓝桥杯 2023 省 Python B] 树上选点|普及+

本文涉及知识点

C++动态规划
C++BFS算法

P12382 [蓝桥杯 2023 省 Python B] 树上选点

题目描述

给定一棵树,树根为 1 1 1,每个点的点权为 V i V_i Vi​。

你需要找出若干个点 P i P_i Pi​,使得:

  1. 每两个点 P x , P y P_x,P_y Px​,Py​ 互不相邻;
  2. 每两个点 P x , P y P_x,P_y Px​,Py​ 与树根的距离互不相同;
  3. 找出的点的点权之和尽可能大。

请输出找到的这些点的点权和的最大值。

输入格式

输入的第一行包含一个整数 n n n。

第二行包含 n − 1 n-1 n−1 个整数 F i F_i Fi​,相邻整数之间使用一个空格分隔,分别表示第 2 至 n n n 个结点的父结点编号。

第三行包含 n n n 个整数 V i V_i Vi​,相邻整数之间使用一个空格分隔,分别表示每个结点的点权。

输出格式

输出一行包含一个整数表示答案。

输入输出样例 #1

输入 #1

5 1 2 3 2 2 1 9 3 5 

输出 #1

11 

说明/提示

评测用例规模与约定

  • 对于 40 % 40\% 40% 的评测用例, n ≤ 5000 n \leq 5000 n≤5000;
  • 对于所有评测用例, 1 ≤ n ≤ 2 × 10 5 1 \leq n \leq 2 \times 10^5 1≤n≤2×105, 1 ≤ F i < i 1 \leq F_i < i 1≤Fi​<i, 1 ≤ V i ≤ 10 4 1 \leq V_i \leq 10^4 1≤Vi​≤104。

P12382 [蓝桥杯 2023 省 Python B] 树上选点

性质一:任意父子节点,不会被同时选择。
性质二:任意层次都只有一个节点。
特殊情况:3个节点的链,根是2,权重分别为2,1,2。选择0层2层的2,不选第1层。
pre和cur记录上一层和当前层的信息。 c u r i = w w i , n o d e i cur_i={ww_i,node_i} curi​=wwi​,nodei​,表示node被选择,node的所有祖先所在层都已经选择的最大权值和是 w w i ww_i wwi​。pre类型,不赘述。
BFS出各层次的节点。初始pre = {{根节点权重,0},{0,-1}},我习惯编号从0开始。
pre降序, curnode in 第i层的节点,如果curnode的父节点不是pre[0].node,j=0,否则j=1。 cur.emplace{pre[j].ww+当前节点权重,curnode}
cur.emplace({pre[0].ww,-1})表示不选择。
本题和普通滚动向量优化空间不同,cur swap之前,降序排序,只保留cur[0…1],其它删除。
瓶颈在排序,时间复杂度:O(nlogn)。

代码

核心代码

#include<iostream>#include<sstream>#include<vector>#include<map>#include<unordered_map>#include<set>#include<unordered_set>#include<string>#include<algorithm>#include<functional>#include<queue>#include<stack>#include<iomanip>#include<numeric>#include<math.h>#include<climits>#include<assert.h>#include<cstring>#include<list>#include<array>#include<bitset>usingnamespace std;template<classT1,classT2> std::istream&operator>>(std::istream& in, pair<T1, T2>& pr){ in >> pr.first >> pr.second;return in;}template<classT1,classT2,classT3> std::istream&operator>>(std::istream& in, tuple<T1, T2, T3>& t){ in >>get<0>(t)>>get<1>(t)>>get<2>(t);return in;}template<classT1,classT2,classT3,classT4> std::istream&operator>>(std::istream& in, tuple<T1, T2, T3, T4>& t){ in >>get<0>(t)>>get<1>(t)>>get<2>(t)>>get<3>(t);return in;}template<classT1,classT2,classT3,classT4,classT5,classT6,classT7> std::istream&operator>>(std::istream& in, tuple<T1, T2, T3, T4,T5,T6,T7>& t){ in >>get<0>(t)>>get<1>(t)>>get<2>(t)>>get<3>(t)>>get<4>(t)>>get<5>(t)>>get<6>(t);return in;}template<classT=int> vector<T>Read(){int n; cin >> n; vector<T>ret(n);for(int i =0; i < n; i++){ cin >> ret[i];}return ret;}template<classT=int> vector<T>ReadNotNum(){ vector<T> ret; T tmp;while(cin >> tmp){ ret.emplace_back(tmp);if('\n'== cin.get()){break;}}return ret;}template<classT=int> vector<T>Read(int n){ vector<T>ret(n);for(int i =0; i < n; i++){ cin >> ret[i];}return ret;}template<int N =1'000'000>classCOutBuff{public:COutBuff(){ m_p = puffer;}template<classT>voidwrite(T x){int num[28], sp =0;if(x <0)*m_p++='-', x =-x;if(!x)*m_p++=48;while(x) num[++sp]= x %10, x /=10;while(sp)*m_p++= num[sp--]+48;AuotToFile();}voidwritestr(constchar* sz){strcpy(m_p, sz); m_p +=strlen(sz);AuotToFile();}inlinevoidwrite(char ch){*m_p++= ch;AuotToFile();}inlinevoidToFile(){fwrite(puffer,1, m_p - puffer,stdout); m_p = puffer;}~COutBuff(){ToFile();}private:inlinevoidAuotToFile(){if(m_p - puffer > N -100){ToFile();}}char puffer[N],* m_p;};template<int N =1'000'000>classCInBuff{public:inlineCInBuff(){}inline CInBuff<N>&operator>>(char& ch){FileToBuf();while(('\r'==*S)||('\n'==*S)||(' '==*S)){ S++;}//忽略空格和回车 ch =*S++;return*this;}inline CInBuff<N>&operator>>(int& val){FileToBuf();intx(0),f(0);while(!isdigit(*S)) f |=(*S++=='-');while(isdigit(*S)) x =(x <<1)+(x <<3)+(*S++^48); val = f ?-x : x; S++;//忽略空格换行 return*this;}inline CInBuff&operator>>(longlong& val){FileToBuf();longlongx(0);intf(0);while(!isdigit(*S)) f |=(*S++=='-');while(isdigit(*S)) x =(x <<1)+(x <<3)+(*S++^48); val = f ?-x : x; S++;//忽略空格换行return*this;}template<classT1,classT2>inline CInBuff&operator>>(pair<T1, T2>& val){*this>> val.first >> val.second;return*this;}template<classT1,classT2,classT3>inline CInBuff&operator>>(tuple<T1, T2, T3>& val){*this>>get<0>(val)>>get<1>(val)>>get<2>(val);return*this;}template<classT1,classT2,classT3,classT4>inline CInBuff&operator>>(tuple<T1, T2, T3, T4>& val){*this>>get<0>(val)>>get<1>(val)>>get<2>(val)>>get<3>(val);return*this;}template<classT=int>inline CInBuff&operator>>(vector<T>& val){int n;*this>> n; val.resize(n);for(int i =0; i < n; i++){*this>> val[i];}return*this;}template<classT=int> vector<T>Read(int n){ vector<T>ret(n);for(int i =0; i < n; i++){*this>> ret[i];}return ret;}template<classT=int> vector<T>Read(){ vector<T> ret;*this>> ret;return ret;}private:inlinevoidFileToBuf(){constint canRead = m_iWritePos -(S - buffer);if(canRead >=100){return;}if(m_bFinish){return;}for(int i =0; i < canRead; i++){ buffer[i]= S[i];//memcpy出错 } m_iWritePos = canRead; buffer[m_iWritePos]=0; S = buffer;int readCnt =fread(buffer + m_iWritePos,1, N - m_iWritePos,stdin);if(readCnt <=0){ m_bFinish =true;return;} m_iWritePos += readCnt; buffer[m_iWritePos]=0; S = buffer;}int m_iWritePos =0;bool m_bFinish =false;char buffer[N +10],* S = buffer;};classCBFSLeve{public:static vector<int>Leve(const vector<vector<int>>& neiBo, vector<int> start){ vector<int>leves(neiBo.size(),-1);for(constauto& s : start){ leves[s]=0;}for(int i =0; i < start.size(); i++){for(constauto& next : neiBo[start[i]]){if(-1!= leves[next]){continue;} leves[next]= leves[start[i]]+1; start.emplace_back(next);}}return leves;}template<classNextFun>static vector<int>Leve(int N, NextFun nextFun, vector<int> start){ vector<int>leves(N,-1);for(constauto& s : start){ leves[s]=0;}for(int i =0; i < start.size(); i++){auto nexts =nextFun(start[i]);for(constauto& next : nexts){if(-1!= leves[next]){continue;} leves[next]= leves[start[i]]+1; start.emplace_back(next);}}return leves;}static vector<vector<int>>LeveNodes(const vector<int>& leves){constint iMaxLeve =*max_element(leves.begin(), leves.end()); vector<vector<int>>ret(iMaxLeve +1);for(int i =0; i < leves.size(); i++){ ret[leves[i]].emplace_back(i);}return ret;};static vector<int>LeveSort(const vector<int>& leves){constint iMaxLeve =*max_element(leves.begin(), leves.end()); vector<vector<int>>leveNodes(iMaxLeve +1);for(int i =0; i < leves.size(); i++){ leveNodes[leves[i]].emplace_back(i);} vector<int> ret;for(constauto& v : leveNodes){ ret.insert(ret.end(), v.begin(), v.end());}return ret;};};classSolution{public:intAns(vector<int> par,const vector<int>& ws){constint N = ws.size(); par.insert(par.begin(),0); vector<vector<int>>neiBo(N);for(int i =0; i < N; i++){ par[i]--;if(0== i){continue;} neiBo[par[i]].emplace_back(i);}auto leves =CBFSLeve::Leve(neiBo,{0});auto leveNodes =CBFSLeve::LeveNodes(leves); vector<pair<int,int>> pre ={{ws[0],0},{0,-1}};for(int i =1; i < leveNodes.size(); i++){ vector<pair<int,int>> cur ={{pre[0].first,-1}};for(constauto& curNode : leveNodes[i]){constint j =(par[curNode]== pre[0].second)?1:0; cur.emplace_back(pre[j].first + ws[curNode], curNode);}sort(cur.begin(), cur.end(),greater<>()); cur.erase(cur.begin()+2, cur.end()); pre.swap(cur);}return pre[0].first;}};intmain(){#ifdef_DEBUGfreopen("a.in","r",stdin);#endif// DEBUG  ios::sync_with_stdio(0); cin.tie(nullptr);//CInBuff<> in; COutBuff<10'000'000> ob; int N; cin >> N;auto par =Read<int>(N -1);auto ws =Read<int>(N);#ifdef_DEBUG //printf("N=%d",N);Out(par,",par=");Out(ws,",ws=");//Out(P, ",P=");/*Out(edge, ",edge="); Out(que, ",que=");*///Out(ab, ",ab=");//Out(par, "par=");//Out(que, "que=");//Out(B, "B=");#endif// DEBUG auto res =Solution().Ans(par,ws); cout << res;return0;};

单元测试

vector<int> par , ws ;TEST_METHOD(TestMethod1){ par ={1,2,3,2}, ws ={2,1,9,3,5};auto res =Solution().Ans(par,ws);AssertEx(11, res);}

# 扩展阅读

我想对大家说的话
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛
失败+反思=成功 成功+反思=成功

视频课程

先学简单的课程,请移步ZEEKLOG学院,听白银讲师(也就是鄙人)的讲解。
https://edu.ZEEKLOG.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.ZEEKLOG.net/lecturer/6176

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。

Read more

Material Files:Android上最优雅的开源文件管理器终极指南 [特殊字符]️

Material Files:Android上最优雅的开源文件管理器终极指南 🗂️ 【免费下载链接】MaterialFilesMaterial Design file manager for Android 项目地址: https://gitcode.com/gh_mirrors/ma/MaterialFiles Material Files是一款专为Android设计的Material Design风格文件管理器,它不仅界面美观,而且功能强大,完全免费开源!无论你是新手还是资深用户,这款应用都能为你提供流畅的文件管理体验。✨ 为什么选择Material Files?🤔 极致美观的用户界面 Material Files采用了Google Material Design设计语言,整个界面简洁大方,色彩搭配和谐。无论是浅色主题还是深色主题,都能给你带来愉悦的视觉体验。 完全开源安全可靠 作为开源项目,Material Files的代码完全公开透明,你可以放心使用而不用担心隐私问题。项目的所有功能都是免费的,没有任何隐藏费用! 快速安装步骤 📱 方法一:通过G

By Ne0inhk
Linux系统学习【深入剖析Git的原理和使用(上)】

Linux系统学习【深入剖析Git的原理和使用(上)】

🔥承渊政道:个人主页 ❄️个人专栏: 《C语言基础语法知识》《数据结构与算法》 《C++知识内容》《Linux系统知识》 ✨逆境不吐心中苦,顺境不忘来时路!🎬 博主简介: 引言:在软件开发的全流程中,版本控制是保障协作效率、规避开发风险的核心基石,而Git作为目前最流行、最强大的分布式版本控制系统,早已渗透到从个人开发到大型企业级项目的每一个环节.无论是多人协作时的代码冲突解决、开发过程中的版本回溯,还是跨环境的代码同步、分支管理,Git都以其高效、安全、灵活的特性,成为开发者必备的核心工具.然而,多数开发者对Git的使用仍停留在“会用基础命令”的层面——知道用git add提交暂存、git commit提交本地、git push推送远程,却未必理解这些命令背后的底层逻辑:暂存区(Stage)、本地仓库(Local Repository)、远程仓库(Remote Repository)之间的数据流是怎样的?Git如何高效追踪文件的每一次变更?分布式架构与SVN等集中式版本控制系统相比,核心优势到底体现在哪里? 基于此,

By Ne0inhk

GitHub汉化插件完整指南:打造个性化中文开发环境

GitHub汉化插件完整指南:打造个性化中文开发环境 【免费下载链接】github-chineseGitHub 汉化插件,GitHub 中文化界面。 (GitHub Translation To Chinese) 项目地址: https://gitcode.com/gh_mirrors/gi/github-chinese 还在为GitHub的英文界面感到困扰吗?GitHub汉化插件能够将整个平台界面完美转换为中文环境,让技术学习和项目管理变得更加轻松自然。这款开源工具不仅支持完整的界面中文化,还提供亮色与暗色主题的完美适配,为你打造个性化的开发体验。 🚀 快速上手安装步骤 第一步:安装脚本管理器 这是运行汉化插件的基础环境,推荐选择: * Tampermonkey:功能丰富,社区活跃 * Violentmonkey:开源轻量,隐私友好 安装方法: 1. 打开浏览器应用商店 2. 搜索对应名称并点击安装 3. 确认工具栏出现相应图标 安全提示:务必从官方渠道下载,避免使用第三方来源的安装包。 第二步:获取汉化脚本 有两种方式可以获取最新的汉化脚本:

By Ne0inhk

图表数据提取神器:WebPlotDigitizer 快速上手全攻略

图表数据提取神器:WebPlotDigitizer 快速上手全攻略 【免费下载链接】WebPlotDigitizerComputer vision assisted tool to extract numerical data from plot images. 项目地址: https://gitcode.com/gh_mirrors/web/WebPlotDigitizer 还在为从图表图片中提取数据而烦恼吗?WebPlotDigitizer 这款计算机视觉辅助工具能够帮你快速从各种图表图像中提取精确的数值数据。无论你是科研人员需要从论文图表获取实验数据,还是工程师要从技术报告提取趋势曲线,这个工具都能在几分钟内完成数据转换。 新手必备:快速搭建你的数据提取环境 在开始使用之前,你需要确保系统环境准备就绪。首先检查 Node.js 版本,建议使用 v14 或更高版本: node -v npm -v 如果未安装,Ubuntu 用户可以通过以下命令快速安装: sudo apt update

By Ne0inhk