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

【图论 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)

代码

核心代码

classCNeiBo{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(constauto&[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(constauto& 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(constauto& 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(constauto&[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;}};classSolution{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;}voidDFS(constint cur,constint par,vector<vector<int>>& neiBo){int iChild =0;for(constauto& 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;}voidDFS2(constint cur,constint par, vector<vector<int>>& neiBo){if(-1== par){ m_ans[cur]= m_vSub[cur];}else{constint 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(constauto& next : neiBo[cur]){if(par == next){continue;}DFS2(next, cur, neiBo);}} vector<int> m_vSub, good,m_ans;};

单元测试

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);}

扩展阅读

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

视频课程

先学简单的课程,请移步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

用 Codex + GitHub Spec-Kit 做一次“规格驱动开发”实战

用 Codex + GitHub Spec-Kit 做一次“规格驱动开发”实战

* 用 Codex + GitHub Spec-Kit 做一次“规格驱动开发”实战 * 1) 初始化:把 spec-kit 工作区真正建起来(多种方式) * 方式 A:uvx 一次性运行(推荐) * 方式 B:uv tool install(全局安装 specify) * 方式 C:pipx 安装(Python 工具常用法) * 2) 初始化后,正确的目录结构长什么样( * 3) 在 Codex 里跑 speckit:统一输入规则(非常重要) * 4) 标准流水线:Constitution → Spec → Plan → Tasks → Implement * Step 1:

By Ne0inhk
VSCode Github Copilot使用OpenAI兼容的自定义模型方法

VSCode Github Copilot使用OpenAI兼容的自定义模型方法

背景 VSCode 1.105.0发布了,但是用户最期待的Copilot功能却没更新!!! (Github Copilot Chat 中使用OpenAI兼容的自定义模型。) 🔥官方也关闭了Issue,并且做了回复,并表示未来也不会更新这个功能: “实际上,这个功能在可预见的未来只面向内部人员开放,作为一种“高级”实验功能。是否实现特定模型提供者的功能,我们交由扩展作者自行决定。仅限内部人员使用可以让我们快速推进,并提供一种可能并非始终百分之百完善,但能够持续改进并快速修复 bug 的体验。如果这个功能对你很重要,我建议切换到内部版本 insider。” 🤗 官方解决方案:安装VSCode扩展支持 你们完全不用担心只需要在 VS Code 中安装扩展:OAI Compatible Provider for Copilot 通过任何兼容 OpenAI 的提供商驱动的 GitHub Copilot Chat,使用前沿开源大模型,如 Kimi K2、DeepSeek

By Ne0inhk
使用 VS Code 将项目代码上传到 Gitee 的完整指南

使用 VS Code 将项目代码上传到 Gitee 的完整指南

在现代软件开发流程中,版本控制是不可或缺的一环。 Gitee(码云)作为国内领先的代码托管平台,为开发者提供了稳定、快速的 Git 服务。 本文将详细介绍如何使用 Visual Studio Code(VS Code)将本地项目代码上传至 Gitee 仓库,涵盖从环境配置、初始化仓库到推送代码的完整流程。 一、准备工作 1. 安装必要工具 * Git:确保你的系统已安装 Git。 可通过终端运行 git --version  或 git -v 验证是否安装成功。 * VS Code:下载并安装 Visual Studio Code。 * Gitee 账号:前往 Gitee 官网 注册账号(如尚未注册)。 2. 安装 VS

By Ne0inhk
使用Git将代码从远程仓库拉取到本地(详细图解、简单易懂)

使用Git将代码从远程仓库拉取到本地(详细图解、简单易懂)

目录 一、前言 二、全流程 一、前言 本博客主要记录一下使用Git将代码从远程仓库拉取到本地的全流程,使用Git拉取代码在学校内多同学合作开发项目或者是实习拉取公司代码等场景都很常见,单纯记录希望对你有帮助 二、全流程 首先在你想要存放代码的位置新建一个文件夹并改名 进入刚刚创建的空文件中,右键然后点击显示更多选项 然后点击Git Bash Here 然后就会出现如图所示的命令行窗口 此时先不用管命令行窗口,找到你要远程仓库所在的平台(我这里以Gitee演示),如图点击克隆/下载按钮 HTTPS下方就是远程仓库的url地址,只要有远程仓库的url地址,只需要在刚刚的命令行窗口打上git clone在将url地址复制在后面再回车即可(Gitee下面的提示也给了,直接复制带git clone的命令就行,没有的话就自己敲git clone) 复制到命令行窗口之后,等待片刻即可 然后点开刚刚创建的文件夹就可以看到拉取下来的代码了,后续用IDEA打开该文件就可以在本地进行开发了

By Ne0inhk