【深度优先搜索】【图论】【树】2646. 最小化旅行的价格总和

作者推荐

【数位dp】【动态规划】【状态压缩】【推荐】1012. 至少有 1 位重复的数字

涉及知识点

深度优先搜索 图论 树

LeetCode2646. 最小化旅行的价格总和

现有一棵无向、无根的树,树中有 n 个节点,按从 0 到 n - 1 编号。给你一个整数 n 和一个长度为 n - 1 的二维整数数组 edges ,其中 edges[i] = [ai, bi] 表示树中节点 ai 和 bi 之间存在一条边。
每个节点都关联一个价格。给你一个整数数组 price ,其中 price[i] 是第 i 个节点的价格。
给定路径的 价格总和 是该路径上所有节点的价格之和。
另给你一个二维整数数组 trips ,其中 trips[i] = [starti, endi] 表示您从节点 starti 开始第 i 次旅行,并通过任何你喜欢的路径前往节点 endi 。
在执行第一次旅行之前,你可以选择一些 非相邻节点 并将价格减半。
返回执行所有旅行的最小价格总和。
示例 1:
输入:n = 4, edges = [[0,1],[1,2],[1,3]], price = [2,2,10,6], trips = [[0,3],[2,1],[2,3]]
输出:23
解释:
上图表示将节点 2 视为根之后的树结构。第一个图表示初始树,第二个图表示选择节点 0 、2 和 3 并使其价格减半后的树。
第 1 次旅行,选择路径 [0,1,3] 。路径的价格总和为 1 + 2 + 3 = 6 。
第 2 次旅行,选择路径 [2,1] 。路径的价格总和为 2 + 5 = 7 。
第 3 次旅行,选择路径 [2,1,3] 。路径的价格总和为 5 + 2 + 3 = 10 。
所有旅行的价格总和为 6 + 7 + 10 = 23 。可以证明,23 是可以实现的最小答案。
示例 2:
输入:n = 2, edges = [[0,1]], price = [2,2], trips = [[0,0]]
输出:1
解释:
上图表示将节点 0 视为根之后的树结构。第一个图表示初始树,第二个图表示选择节点 0 并使其价格减半后的树。
第 1 次旅行,选择路径 [0] 。路径的价格总和为 1 。
所有旅行的价格总和为 1 。可以证明,1 是可以实现的最小答案。
提示:
1 <= n <= 50
edges.length == n - 1
0 <= ai, bi <= n - 1
edges 表示一棵有效的树
price.length == n
price[i] 是一个偶数
1 <= price[i] <= 1000
1 <= trips.length <= 100
0 <= starti, endi <= n - 1

两次深度优先搜索

深度优先搜索计算进过各节点多少次

以任何一个节点(比如:0)为整课树的节点,有如下性质:
性质一:路径一定是:起点 → \rightarrow 公共祖先 → \rightarrow 终点 特例是:起点或终点就是公共祖先。
性质二:如果某棵子树包括某次旅行的起点或终点,则此次旅行必定经过此子树根节点。如果起点和终点都是此子树的节点,也算。 之后就不算了。
如何判断 节点是否属于子树:
DFS 的开始,给节点编号(访问编号)m_vTime[cur],从1到大。没有访问就是默认值0。
DFS结束时,访问编号大于等于m_vTime[cur],是本子树的节点。
m_vNeedVis 记录各节点访问的需要访问的次数。
m_vHasDo 记录那些旅行的公共祖先已经访问。

深度优先搜索枚举半价

{ 子节点节点全价 根节点半价 m i n ( 子节点节点全价,子节点节点半价 ) 根节点全价 \begin{cases} 子节点节点全价 & 根节点半价 \\ min(子节点节点全价,子节点节点半价) & 根节点全价 \\ \end{cases} {子节点节点全价min(子节点节点全价,子节点节点半价)根节点半价根节点全价
DFS2返回值两个:
一,全价、半价的较小值。
二,全价的最小值。

代码

核心代码

class CNeiBo2
{
public:CNeiBo2(int n, bool bDirect, int iBase = 0) :m_iN(n), m_bDirect(bDirect), m_iBase(iBase){m_vNeiB.resize(n);}CNeiBo2(int n, vector<vector<int>>& edges, bool bDirect, int iBase = 0) :m_iN(n), m_bDirect(bDirect), m_iBase(iBase){m_vNeiB.resize(n);for (const auto& v : edges){m_vNeiB[v[0] - iBase].emplace_back(v[1] - iBase);if (!bDirect){m_vNeiB[v[1] - iBase].emplace_back(v[0] - iBase);}}}inline void Add(int iNode1, int iNode2){iNode1 -= m_iBase;iNode2 -= m_iBase;m_vNeiB[iNode1].emplace_back(iNode2);if (!m_bDirect){m_vNeiB[iNode2].emplace_back(iNode1);}}const int m_iN;const bool m_bDirect;const int m_iBase;vector<vector<int>> m_vNeiB;
};class Solution {
public:int minimumTotalPrice(int n, vector<vector<int>>& edges, vector<int>& price, vector<vector<int>>& trips) {CNeiBo2 neiBo(n, edges, false);m_vNeedVis.resize(n);m_vHasDo.resize(trips.size());m_vTime.resize(n);m_trips = trips;m_price = price;DFS(neiBo.m_vNeiB, 0, -1);return DFS2(neiBo.m_vNeiB, 0, -1).first;}void DFS(vector<vector<int>>& neiBo, int cur, int par){m_vTime[cur] = ++m_llTime;for (const auto& next : neiBo[cur]){if (next == par){continue;}DFS(neiBo, next, cur);}for (int i = m_trips.size()-1 ; i >=0 ; i--){if (m_vHasDo[i]){continue;}const auto& v = m_trips[i];if ((m_vTime[v[0]] >= m_vTime[cur]) || (m_vTime[v[1]] >= m_vTime[cur])){m_vNeedVis[cur]++;if ((m_vTime[v[0]] >= m_vTime[cur]) && (m_vTime[v[1]] >= m_vTime[cur])){ m_vHasDo[i] = true;}}}}pair<int,int> DFS2(vector<vector<int>>& neiBo, int cur, int par){int  i2 = m_price[cur]*m_vNeedVis[cur],i1 =i2/2;for (const auto& next : neiBo[cur]){if (next == par){continue;}auto [i11,i21] = DFS2(neiBo, next, cur);i1 += i21;i2 += i11;}return make_pair(min(i1, i2), i2);}vector<int> m_vNeedVis,m_vTime;// 记录各节点访问的需要访问的次数。vector<bool>	m_vHasDo;// 记录那些旅行的公共祖先已经访问。int m_llTime = 0;vector<vector<int>> m_trips;vector<int> m_price;
};

测试用例


template<class T,class T2>
void Assert(const T& t1, const T2& t2)
{assert(t1 == t2);
}template<class T>
void Assert(const vector<T>& v1, const vector<T>& v2)
{if (v1.size() != v2.size()){assert(false);return;}for (int i = 0; i < v1.size(); i++){Assert(v1[i], v2[i]);}}int main()
{	int n;vector<int>  price;vector<vector<int>> edges, trips;{Solution sln;n = 4, edges = { {0,1},{1,2},{1,3} }, price = { 2,2,10,6 }, trips = { {0,3},{2,1},{2,3} };auto res = sln.minimumTotalPrice(n, edges, price, trips);Assert(res,23);}{Solution sln;n = 2, edges = { {0,1} }, price = { 2,2 }, trips = { {0,0} };auto res = sln.minimumTotalPrice(n, edges, price, trips);Assert(res, 1);}{Solution sln;n = 5, edges = { {1,2},{2,0},{0,3},{3,4} }, price = { 12,26,22,12,2 };trips = { {3,3},{3,2},{3,0},{3,4},{1,1},{2,2},{4,0},{0,2},{2,3},{2,1},{4,2},{0,1},{4,2},{0,4},{0,3},{4,0},{4,0},{3,3},{4,3},{2,2},{4,2},{1,4},{3,2},{4,4},{4,2},{2,3},{4,3},{4,4},{4,2},{0,4},{4,2},{3,4},{4,0},{3,2},{3,1},{2,0},{0,4},{3,4},{2,0},{1,4},{4,2},{4,4},{2,1},{0,1},{4,1},{3,4},{0,4},{2,0},{2,0},{3,3},{4,4},{0,1},{0,1},{0,1},{2,0},{0,1},{3,1},{3,4},{3,4},{4,2},{0,4},{4,4},{3,2},{2,1},{3,2},{1,4},{1,0},{4,2},{4,3},{3,1},{4,4},{3,1},{1,0},{0,0},{0,0},{3,0},{0,2},{2,2},{3,3},{0,3} };;auto res = sln.minimumTotalPrice(n, edges, price, trips);Assert(res, 2037);}}

2023年3月

class CNeiBo2
{
public:
CNeiBo2(int n, vector<vector>& edges, bool bDirect)
{
m_vNeiB.resize(n);
for (const auto& v : edges)
{
m_vNeiB[v[0]].emplace_back(v[1]);
if (!bDirect)
{
m_vNeiB[v[1]].emplace_back(v[0]);
}
}
}
vector<vector> m_vNeiB;
};
class Solution {
public:
int minimumTotalPrice(int n, vector<vector>& edges, vector& price, vector<vector>& trips) {
m_vParent.resize(n);
CNeiBo2 neBo(n, edges, false);
dfs(0, -1, neBo.m_vNeiB);
vector vTotalPrice(n);
for (const vector& trip : trips)
{
const auto& v0 = m_vParent[trip[0]];
const auto& v1 = m_vParent[trip[1]];
int i = 0;
for (; (i < min(v0.size(), v1.size())) && (v0[i] == v1[i]); i++);
vTotalPrice[v0[i - 1]] += price[v0[i - 1]];
for (int j = i; j < v0.size(); j++)
{
vTotalPrice[v0[j]] += price[v0[j]];
}
for (int j = i; j < v1.size(); j++)
{
vTotalPrice[v1[j]] += price[v1[j]];
}
}
int iRet = std::accumulate(vTotalPrice.begin(), vTotalPrice.end(), 0);
return iRet - MaxDFS(0, -1, neBo.m_vNeiB, vTotalPrice, true);
}
void dfs(int iCur, int iParent, const vector<vector>& vNeiBo)
{
if (-1 != iParent)
{
m_vParent[iCur] = m_vParent[iParent];
}
m_vParent[iCur].emplace_back(iCur);
for (const auto& next : vNeiBo[iCur])
{
if (iParent == next)
{
continue;
}
dfs(next, iCur, vNeiBo);
}
}
int MaxDFS(int iCur, int iParent, const vector<vector>& vNeiBo, const vector& vTotalPrice,bool bCanRoot)
{
int iRet = 0;
for (const auto& next : vNeiBo[iCur])
{
if (iParent == next)
{
continue;
}
iRet += MaxDFS(next, iCur, vNeiBo, vTotalPrice,true);
}
if ((!bCanRoot) || (0 == vTotalPrice[iCur]))
{
return iRet;
}
int iRet2 = vTotalPrice[iCur] / 2;
for (const auto& next : vNeiBo[iCur])
{
if (iParent == next)
{
continue;
}
iRet2 += MaxDFS(next, iCur, vNeiBo, vTotalPrice, false);
}
return max(iRet, iRet2);
}
vector<vector> m_vParent;
};

扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771

如何你想快

速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

相关

下载

想高屋建瓴的学习算法,请下载《喜缺全书算法册》doc版
https://download.csdn.net/download/he_zhidan/88348653

我想对大家说的话
闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛

测试环境

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/474564.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DS:八大排序之堆排序、冒泡排序、快速排序

创作不易&#xff0c;友友们给个三连吧&#xff01;&#xff01; 一、堆排序 堆排序已经在博主关于堆的实现过程中详细的讲过了&#xff0c;大家可以直接去看&#xff0c;很详细,这边不介绍了 DS&#xff1a;二叉树的顺序结构及堆的实现-CSDN博客 直接上代码&#xff1a; …

教你如何用vs2022 制作自己的模板

首先现将我们的模板代码写好 如图&#xff1a; 选择项目&#xff1a; 选择导出模板&#xff1b; 如下图 如果只有一个项 两个都可以选&#xff0c;如果有多个项&#xff0c;就只能选第一个 然后给自己的模板起个名字 注意&#xff1a;不能有特殊符号&#xff0c;也不能有中…

win32汇编获取系统信息

.data fmt db "页尺寸&#xff1a;%d",0 db "" lpsystem SYSTEM_INFO <?> szbuf db 200 dup(0) .const szCaption db 系统信息,0 .code start: invoke GetSystemInfo,addr lpsystem …

渲染案例 |《甲辰春来,福暖四季》蓝海创意云助力央视新闻频道打造2024龙年除夕视觉盛宴

随着2024年甲辰龙年的脚步渐近&#xff0c;中央广播电视总台新闻频道精心策划的除夕特别节目《甲辰春来&#xff0c;福暖四季》于2月9日上午9点准时与全国观众见面。这一场充满传统韵味与现代气息的视觉盛宴&#xff0c;不仅展现了浓厚的节日氛围&#xff0c;更在技术上实现了突…

如何选择合适的三防平板?

一、随着移动互联网的快速发展&#xff0c;平板电脑作为一种轻便、易携带的移动设备已经成为了人们生活中必不可少的一部分。在一些特殊的工作环境下&#xff0c;例如工地、野外等场景&#xff0c;常规的平板电脑可能无法满足使用要求&#xff0c;因此人们需要选择一款具有防水…

抖音电商的5种直播组货策略

1.三种典型的商家类型 2.五种典型的直播组货策略 &#xff08;1&#xff09;单一款式组货 &#xff08;2&#xff09;垂直类组货 &#xff08;3&#xff09;多品类组货 &#xff08;4&#xff09;品牌专场组货 &#xff08;5&#xff09;平台专场组货

人工智能学习与实训笔记(十五):Scikit-learn库的基础与使用

人工智能专栏文章汇总&#xff1a;人工智能学习专栏文章汇总-CSDN博客 本篇目录 一、介绍 1. 1 Scikit-learn的发展历程及定义 1.2 理解算法包、算法库及算法框架之间的区别和联系 二、Scikit-learn官网结构 三、安装与设置 3.1 Python环境的安装与配置 3.2 Scikit-lea…

人机交互新研究:MIT开发了结合脑电和眼电的新式眼镜,与机器狗交互

还记得之前的AI读心术吗&#xff1f;最近&#xff0c;「心想事成」的能力再次进化&#xff0c; ——人类可以通过自己的想法直接控制机器人了&#xff01; 来自麻省理工的研究人员发表了Ddog项目&#xff0c;通过自己开发的脑机接口&#xff08;BCI&#xff09;设备&#xff…

优化嵌入式系统电源管理以提高稳定性

&#xff08;本文为简单介绍&#xff0c;观点源于网络&#xff09; 在嵌入式系统的领域中&#xff0c;电源管理扮演着至关重要的角色&#xff0c;关乎系统稳定性与用户体验。如果电源管理做得不好&#xff0c;就可能导致系统不稳定、数据丢失&#xff0c;甚至硬件损坏。电源管…

马坦门施端生武术学社年庆:武术界的“武林大会”

航拍图片由温州中视文化传播公司提供 话说&#xff0c;龙年正月初七这一天&#xff0c;正当人们在喜庆的年味中慢慢苏醒&#xff0c;位于浙江温州瑞安市曹村镇女岙村的马坦门施端生武术学社可是一大早就热闹了起来。因为&#xff0c;他们正在举办一场盛大的庆典&#xff0c;庆祝…

Qt的安装

下载安装包 我们选择Open Source的版本。 可以从https://www.qt.io/download-open-source直接下载最新版本&#xff0c;对于想要选择特定版本&#xff0c;或者尝试beta版功能的用户&#xff0c;也可以从https://download.qt.io选择自己需要的版本。 如果你的网速可以的话&am…

斯坦福大学最新研究:61%留学生写的英语论文,会被ChatGPT检测器判为AI生成!判断错误率高达76%

ChatGPT的推出引发了广泛的讨论和应用&#xff0c;它的用途多种多样。有些人利用它寻求人生指导&#xff0c;有些人把它当作搜索引擎使用&#xff0c;还有人用它来撰写论文。 在美国&#xff0c;一些大学已经明确禁止学生使用ChatGPT完成作业&#xff0c;并且开发了多种软件工…