【图论 DFS 换根法】3772. 子图的最大得分|2235

本文涉及知识点

C++图论 换根法

LeetCode3772. 子图的最大得分

给你一个 无向树 ,它包含 n 个节点,编号从 0 到 n - 1。树由一个长度为 n - 1 的二维整数数组 edges 描述,其中 edges[i] = [ai, bi] 表示在节点 ai 和节点 bi 之间有一条边。

另给你一个长度为 n 的整数数组 good,其中 good[i] 为 1 表示第 i 个节点是好节点,为 0 表示它是坏节点。

定义 子图 的 得分 为子图中好节点的数量减去坏节点的数量。

对于每个节点 i,找到包含节点 i 的所有 连通子图 中可能的最大得分。

返回一个长度为 n 的整数数组,其中第 i 个元素是节点 i 的 最大得分 。

子图 是原图的一个子集,其顶点和边均来自原图。

连通子图 是一个子图,其中每一对顶点都可以通过该子图的边相互到达。

示例 1:

输入: n = 3, edges = [[0,1],[1,2]], good = [1,0,1]

输出: [1,1,1]

解释:

绿色节点是好节点,红色节点是坏节点。

对于每个节点,包含它的最佳连通子图是整棵树,该树有 2 个好节点和 1 个坏节点,得分为 1。

包含某个节点的其他连通子图可能有相同的得分。

示例 2:

输入: n = 5, edges = [[1,0],[1,2],[1,3],[3,4]], good = [0,1,0,1,1]

输出: [2,3,2,3,3]

解释:

节点 0:最佳连通子图由节点 0, 1, 3, 4 组成,其中有 3 个好节点和 1 个坏节点,得分为 3 - 1 = 2。

节点 1、3 和 4:最佳连通子图由节点 1, 3, 4 组成,其中有 3 个好节点,得分为 3。

节点 2:最佳连通子图由节点 1, 2, 3, 4 组成,其中有 3 个好节点和 1 个坏节点,得分为 3 - 1 = 2。

示例 3:

输入: n = 2, edges = [[0,1]], good = [0,0]

输出: [-1,-1]

解释:

对于每个节点,包含另一节点只会增加一个坏节点,因此每个节点的最佳得分为 -1。

提示:
2 < = n < = 10 5 2 <= n <= 10^5 2<=n<=105

edges.length == n - 1

edges[i] = [ai, bi]

0 <= ai, bi < n

good.length == n

0 <= good[i] <= 1

输入保证 edges 表示一棵有效树。

换根法

以任意节点(如0)为根。
a n s i ans_i ansi任意包含节点i的联通区域的最大分数。
s u b i sub_i subi任意包含节点i,不包括i的父节点的联通区域的最大分数。

一轮DFS(后序遍历)可以计算出sub。

一轮DFS(前序遍历)可以计算出ans。

good[i]如果是0,改成-1.
性质一 : s u b i = g o o d [ i ] + ∑ j 是 i 的孩子 m a x ( 0 , s u b [ j ] ) sub_i = good[i] + \sum^{j是i的孩子} max(0,sub[j]) subi=good[i]+∑j是i的孩子max(0,sub[j])
性质二 : a n s 0 = s u b 0 ans_0=sub_0 ans0=sub0。
性质三 :令par是cur节点父节点。x是包括par节点,不包括cur节点的最大得分。

如果 s u b c u r > 0 sub_{cur}>0 subcur>0,则 x = a n s p a r − s u b c u r ans_{par}-sub_{cur} anspar−subcur;否则x = a n s p a r ans_{par} anspar
a n s c u r = s u b c u r + m a x ( 0 , x ) ans_{cur}=sub_{cur}+max(0,x) anscur=subcur+max(0,x)

代码

核心代码

cpp 复制代码
class CNeiBo
{
public:	
	static vector<vector<int>> Two(int n, const vector<pair<int, int>>& edges, bool bDirect, int iBase = 0)
	{
		vector<vector<int>>  vNeiBo(n);
		for (const auto& [i1, i2] : edges)
		{
			vNeiBo[i1 - iBase].emplace_back(i2 - iBase);
			if (!bDirect)
			{
				vNeiBo[i2 - iBase].emplace_back(i1 - iBase);
			}
		}
		return vNeiBo;
	}
	static vector<vector<int>> Two(int n, const 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<std::pair<int, int>>> Three(int n, const vector<tuple<int,int,int>>& edges, bool bDirect, int iBase = 0)
	{
		vector<vector<std::pair<int, int>>> vNeiBo(n);
		for (const auto& [u,v,w] : edges)
		{
			vNeiBo[u - iBase].emplace_back(v - iBase, w);
			if (!bDirect)
			{
				vNeiBo[v - iBase].emplace_back(u - iBase, w);
			}
		}
		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 Solution {
		public:
			vector<int> maxSubgraphScore(int n, vector<vector<int>>& edges, vector<int>& good) {
				this->good = good;
				for (auto& i : this->good) {
					if (0 == i) { i = -1; }
				}
				m_vSub.resize(n);
				m_ans.resize(n);				
				auto neiBo = CNeiBo::Two(n, edges, false);
				DFS(0, -1, neiBo);
				DFS2(0, -1, neiBo);
				return m_ans;
			}
			void DFS(const int cur,const int par,vector<vector<int>>& neiBo) {
				int iChild = 0;
				for (const auto& next : neiBo[cur]) {
					if (par == next) { continue; }
					DFS(next, cur, neiBo);
					if (m_vSub[next] > 0)
					{
						iChild += m_vSub[next];
					}
				}
				m_vSub[cur] = good[cur] + iChild;
			}
			void DFS2(const int cur, const int par, vector<vector<int>>& neiBo) {
				if (-1 == par) {
					m_ans[cur] = m_vSub[cur];
				}
				else {
					const int parS = (m_vSub[cur] > 0) ? (m_ans[par] - m_vSub[cur]) : m_ans[par];
					m_ans[cur] = m_vSub[cur];
					if (parS > 0) { m_ans[cur] += parS; }
				}
				for (const auto& next : neiBo[cur]) {
					if (par == next) { continue; }
					DFS2(next, cur, neiBo);					
				}				
			}
			vector<int> m_vSub, good,m_ans;
		}; 

单元测试

cpp 复制代码
int n;
		vector<vector<int>> edges;
		vector<int> good;
		TEST_METHOD(TestMethod001)
		{
			n = 5, edges = { {1,0},{1,2},{1,3},{3,4} }, good = { 0,1,0,1,1 };			
			auto res = Solution().maxSubgraphScore(n, edges, good);
			AssertEx({ 2,3,2,3,3 }, res);
		}
		TEST_METHOD(TestMethod002)
		{
			n = 2, edges = { {0,1} }, good = { 0,0 };;
			auto res = Solution().maxSubgraphScore(n, edges, good);
			AssertEx({ -1,-1 }, res);
		}
		TEST_METHOD(TestMethod003)
		{
			n = 3, edges = { {0,1},{1,2} }, good = { 1,0,1 }			;
			auto res = Solution().maxSubgraphScore(n, edges, good);
			AssertEx({ 1,1,1 }, res);
		}
		TEST_METHOD(TestMethod004)
		{
			n = 3, edges = { {1,0},{0,2} }, good = { 1,1,1 };
			auto res = Solution().maxSubgraphScore(n, edges, good);
			AssertEx({ 3,3,3 }, 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++**实现。

相关推荐
一只大侠的侠10 小时前
Python实现TTAO算法:优化神经网络中的时序预测任务
python·神经网络·算法
千金裘换酒18 小时前
LeetCode 移动零元素 快慢指针
算法·leetcode·职场和发展
wm104318 小时前
机器学习第二讲 KNN算法
人工智能·算法·机器学习
NAGNIP18 小时前
一文搞懂机器学习线性代数基础知识!
算法
NAGNIP18 小时前
机器学习入门概述一览
算法
byxdaz18 小时前
C++内存序
c++
iuu_star19 小时前
C语言数据结构-顺序查找、折半查找
c语言·数据结构·算法
优雅的潮叭19 小时前
c++ 学习笔记之 malloc
c++·笔记·学习
Yzzz-F19 小时前
P1558 色板游戏 [线段树 + 二进制状态压缩 + 懒标记区间重置]
算法