【C++图论 BFS算法】2467. 树上最大得分和路径|2053

本文涉及知识点

C++图论
C++BFS算法

LeetCode2467. 树上最大得分和路径

一个 n 个节点的无向树,节点编号为 0 到 n - 1 ,树的根结点是 0 号节点。给你一个长度为 n - 1 的二维整数数组 edges ,其中 edges[i] = [ai, bi] ,表示节点 ai 和 bi 在树中有一条边。

在每一个节点 i 处有一扇门。同时给你一个都是偶数的数组 amount ,其中 amount[i] 表示:

如果 amount[i] 的值是负数,那么它表示打开节点 i 处门扣除的分数。

如果 amount[i] 的值是正数,那么它表示打开节点 i 处门加上的分数。

游戏按照如下规则进行:

一开始,Alice 在节点 0 处,Bob 在节点 bob 处。

每一秒钟,Alice 和 Bob 分别 移动到相邻的节点。Alice 朝着某个 叶子结点 移动,Bob 朝着节点 0 移动。

对于他们之间路径上的 每一个 节点,Alice 和 Bob 要么打开门并扣分,要么打开门并加分。注意:

如果门 已经打开 (被另一个人打开),不会有额外加分也不会扣分。

如果 Alice 和 Bob 同时 到达一个节点,他们会共享这个节点的加分或者扣分。换言之,如果打开这扇门扣 c 分,那么 Alice 和 Bob 分别扣 c / 2 分。如果这扇门的加分为 c ,那么他们分别加 c / 2 分。

如果 Alice 到达了一个叶子结点,她会停止移动。类似的,如果 Bob 到达了节点 0 ,他也会停止移动。注意这些事件互相 独立 ,不会影响另一方移动。

请你返回 Alice 朝最优叶子结点移动的 最大 净得分。

示例 1:

输入:edges = [[0,1],[1,2],[1,3],[3,4]], bob = 3, amount = [-2,4,2,-4,6]

输出:6

解释:

上图展示了输入给出的一棵树。游戏进行如下:

  • Alice 一开始在节点 0 处,Bob 在节点 3 处。他们分别打开所在节点的门。
    Alice 得分为 -2 。
  • Alice 和 Bob 都移动到节点 1 。
    因为他们同时到达这个节点,他们一起打开门并平分得分。
    Alice 的得分变为 -2 + (4 / 2) = 0 。
  • Alice 移动到节点 3 。因为 Bob 已经打开了这扇门,Alice 得分不变。
    Bob 移动到节点 0 ,并停止移动。
  • Alice 移动到节点 4 并打开这个节点的门,她得分变为 0 + 6 = 6 。
    现在,Alice 和 Bob 都不能进行任何移动了,所以游戏结束。
    Alice 无法得到更高分数。
    示例 2:

输入:edges = [[0,1]], bob = 1, amount = [-7280,2350]

输出:-7280

解释:

Alice 按照路径 0->1 移动,同时 Bob 按照路径 1->0 移动。

所以 Alice 只打开节点 0 处的门,她的得分为 -7280 。

提示:

2 <= n <= 10^5^

edges.length == n - 1

edges[i].length == 2

0 <= ai, bi < n

ai != bi

edges 表示一棵有效的树。

1 <= bob < n

amount.length == n

amount[i] 是范围 [-10^4^, 10^4^] 之间的一个 偶数 。

BFS

本方法用BFS代替DFS,来达到提速的目的。时间复杂度 :O(m),m是边的数量。

一,将边转成邻接表neiBo。

二,BFS邻接表获取各节点层次leve。

三,通过各节点层次获取各层次包括节点leveNodes。

四,通过边和节点层次获取各节点的父节点pars。

五,通过par节点bob到达各节点时间bomTime,无法到达为n+1。

六,层次从小到大处理个节点。ans[cur]记录alice到达时的得分:父节点的得分+本节点得法。如果leve > bomTime[cur],不得分;相等,得一半的分;小于,得全分。

七,求ans[cur]的最大值,cur是叶子节点。 如果只有一个节点,唯一的节点是根,也是叶子。否则,根不是叶子,其它节点是否是叶子节点    ⟺    \iff ⟺ neiBo[cur].size()是否是1。

代码

核心代码

cpp 复制代码
class CNeiBo
{
public:	
	static vector<vector<int>> Two(int n, vector<vector<int>>& edges, bool bDirect, int iBase = 0) 
	{
		vector<vector<int>>  vNeiBo(n);
		for (const auto& v : edges)
		{
			vNeiBo[v[0] - iBase].emplace_back(v[1] - iBase);
			if (!bDirect)
			{
				vNeiBo[v[1] - iBase].emplace_back(v[0] - iBase);
			}
		}
		return vNeiBo;
	}	
	static vector<vector<std::pair<int, int>>> Three(int n, vector<vector<int>>& edges, bool bDirect, int iBase = 0)
	{
		vector<vector<std::pair<int, int>>> vNeiBo(n);
		for (const auto& v : edges)
		{
			vNeiBo[v[0] - iBase].emplace_back(v[1] - iBase, v[2]);
			if (!bDirect)
			{
				vNeiBo[v[1] - iBase].emplace_back(v[0] - iBase, v[2]);
			}
		}
		return vNeiBo;
	}	
	static vector<vector<int>> Mat(vector<vector<int>>& neiBoMat)
	{
		vector<vector<int>> neiBo(neiBoMat.size());
		for (int i = 0; i < neiBoMat.size(); i++)
		{
			for (int j = i + 1; j < neiBoMat.size(); j++)
			{
				if (neiBoMat[i][j])
				{
					neiBo[i].emplace_back(j);
					neiBo[j].emplace_back(i);
				}
			}
		}
		return neiBo;
	}
};

class CBFSLeve {
public :
	static vector<int> Leve(const vector<vector<int>>& neiBo, vector<int> start) {
		vector<int> leves(neiBo.size(), -1);
		for (const auto& s : start) {
			leves[s] = 0;
		}
		for (int i = 0; i < start.size(); i++) {
			for (const auto& next : neiBo[start[i]]) {
				if (-1 != leves[next]) { continue; }
				leves[next] = leves[start[i]] + 1;
				start.emplace_back(next);
			}
		}
		return leves;
	}
	template<class NextFun>
	static vector<int> Leve(int N,NextFun nextFun, vector<int> start) {
		vector<int> leves(N, -1);
		for (const auto& s : start) {
			leves[s] = 0;
		}
		for (int i = 0; i < start.size(); i++) {
			auto nexts = nextFun(start[i]);
			for (const auto& 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) {
		const int 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;
	};
};
class Solution {
		public:
			int mostProfitablePath(vector<vector<int>>& edges, int bob, vector<int>& amount) {
				const int N = amount.size();
				auto neiBo = CNeiBo::Two(N, edges, false);
				auto leves = CBFSLeve::Leve(neiBo, { 0 });
				auto leveNodes = CBFSLeve::LeveNodes(leves);
				vector<int> par(N, -1);
				for (const auto& v : edges) {
					if (leves[v[0]] < leves[v[1]]) {
						par[v[1]] = v[0];
					}
					else {
						par[v[0]] = v[1];
					}
				}
				vector<int> bomTime(N, N + 1);
				for (int i = 0; bob != -1; bob = par[bob], i++) {
					bomTime[bob] = i;
				}
				vector<int> ans(N);
				for (int i = 0; i < leveNodes.size();i++) {
					const auto& nodes = leveNodes[i];
					for (const auto& cur : nodes) {
						if (-1 != par[cur])	{ans[cur] += ans[par[cur]];	}
						if (bomTime[cur] < i) { continue; }
						if (bomTime[cur] > i) {
							ans[cur] += amount[cur];
						}
						else {
							ans[cur] += amount[cur] / 2;
						}
					}
				}
				int iAns = INT_MIN;
				for (int i = 1; i < N; i++) {
					if (neiBo[i].size() != 1)continue;
					iAns = max(iAns, ans[i]);
				}
				return iAns;
			}
		};

单元测试

cpp 复制代码
vector<vector<int>> edges;
		int bob ;
		vector<int> amount;
		TEST_METHOD(TestMethod1)
		{
			edges = { {0,1},{1,2},{1,3},{3,4} }, bob = 3, amount = { -2,4,2,-4,6 };
			auto res = Solution().mostProfitablePath(edges, bob, amount);
			AssertEx(6, res);
		}
		TEST_METHOD(TestMethod2)
		{
			edges = { {0,1} }, bob = 1, amount = { -7280,2350 };
			auto res = Solution().mostProfitablePath(edges, bob, amount);
			AssertEx(-7280, res);
		}	

扩展阅读

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

视频课程

先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771

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

测试环境

操作系统:win7 开发环境: VS2019 C++17

或者 操作系统:win10 开发环境: VS2022 C++17

如无特殊说明,本算法用**C++**实现。

相关推荐
hn小菜鸡36 分钟前
LeetCode 2320.统计放置房子的方式数
算法·leetcode·职场和发展
高一学习c++会秃头吗37 分钟前
leetcode_547 省份数量
算法·leetcode·职场和发展
天赐细莲1 小时前
(仓颉) Cangjie 刷力扣基础语法小结
数据结构·算法·leetcode·职场和发展
程序猿阿伟1 小时前
《C++巧铸随机森林:开启智能决策新境界》
开发语言·c++·随机森林
阿客不是客1 小时前
深入计算机语言之C++:STL之list的模拟实现
数据结构·c++·stl
dundunmm1 小时前
论文阅读:Statistical Comparisons of Classifiers over Multiple Data Sets
论文阅读·人工智能·算法·机器学习·评估方法
pzx_0011 小时前
【论文阅读】相似误差订正方法在风电短期风速预报中的应用研究
开发语言·论文阅读·python·算法·leetcode·sklearn
祖坟冒青烟2 小时前
Qt 的构建系统
c++
小雄abc2 小时前
决定系数R2 浅谈三 : 决定系数R2与相关系数r的关系、决定系数R2是否等于相关系数r的平方
经验分享·笔记·深度学习·算法·机器学习·学习方法·论文笔记
uyeonashi2 小时前
【C++】刷题强训(day14)--乒乓球匡、组队竞赛、删除相邻数字的最大分数
开发语言·c++·算法·哈希算法