【树】【异或】【深度优先】【DFS时间戳】2322. 从树中删除边的最小分数

news2024/12/22 17:33:14

作者推荐

【二分查找】【C++算法】378. 有序矩阵中第 K 小的元素

涉及知识点

树 异或 DFS时间戳

LeetCode2322. 从树中删除边的最小分数

存在一棵无向连通树,树中有编号从 0 到 n - 1 的 n 个节点, 以及 n - 1 条边。
给你一个下标从 0 开始的整数数组 nums ,长度为 n ,其中 nums[i] 表示第 i 个节点的值。另给你一个二维整数数组 edges ,长度为 n - 1 ,其中 edges[i] = [ai, bi] 表示树中存在一条位于节点 ai 和 bi 之间的边。
删除树中两条 不同 的边以形成三个连通组件。对于一种删除边方案,定义如下步骤以计算其分数:
分别获取三个组件 每个 组件中所有节点值的异或值。
最大 异或值和 最小 异或值的 差值 就是这一种删除边方案的分数。
例如,三个组件的节点值分别是:[4,5,7]、[1,9] 和 [3,3,3] 。三个异或值分别是 4 ^ 5 ^ 7 = 6、1 ^ 9 = 8 和 3 ^ 3 ^ 3 = 3 。最大异或值是 8 ,最小异或值是 3 ,分数是 8 - 3 = 5 。
返回在给定树上执行任意删除边方案可能的 最小 分数。
示例 1:
在这里插入图片描述

输入:nums = [1,5,5,4,11], edges = [[0,1],[1,2],[1,3],[3,4]]
输出:9
解释:上图展示了一种删除边方案。

  • 第 1 个组件的节点是 [1,3,4] ,值是 [5,4,11] 。异或值是 5 ^ 4 ^ 11 = 10 。
  • 第 2 个组件的节点是 [0] ,值是 [1] 。异或值是 1 = 1 。
  • 第 3 个组件的节点是 [2] ,值是 [5] 。异或值是 5 = 5 。
    分数是最大异或值和最小异或值的差值,10 - 1 = 9 。
    可以证明不存在分数比 9 小的删除边方案。
    示例 2:
    在这里插入图片描述

输入:nums = [5,5,2,4,4,2], edges = [[0,1],[1,2],[5,2],[4,3],[1,3]]
输出:0
解释:上图展示了一种删除边方案。

  • 第 1 个组件的节点是 [3,4] ,值是 [4,4] 。异或值是 4 ^ 4 = 0 。
  • 第 2 个组件的节点是 [1,0] ,值是 [5,5] 。异或值是 5 ^ 5 = 0 。
  • 第 3 个组件的节点是 [2,5] ,值是 [2,2] 。异或值是 2 ^ 2 = 0 。
    分数是最大异或值和最小异或值的差值,0 - 0 = 0 。
    无法获得比 0 更小的分数 0 。

预备知识

性质一:n个数进行异或运算。各位的结果等于各数本位1的数量是否为奇数。
当前 n 为 2 时:只有四种情况 1 ⊕ 1 = 0 , 0 ⊕ 0 = 0 , 0 ⊕ 1 = 1 , 1 ⊕ 0 = 1 全部符合 当 n > 2 时,任意选两个数,运算后 1 的数量奇偶性不变 当前n为2时:只有四种情况1\oplus1= 0, 0\oplus0= 0, 0\oplus1= 1,1\oplus0= 1 全部符合 \\ 当n>2时,任意选两个数,运算后1的数量奇偶性不变 当前n2时:只有四种情况11=0,00=0,01=1,10=1全部符合n>2时,任意选两个数,运算后1的数量奇偶性不变
推论一: n个数的异或,结果与运算顺序无关。
推论二:异或的逆运算就是本身。

深度优先

以任意节点(比如0)为根,除根节点外,每个节点都有且只有一个父节点。枚举两个非根节点A,B,A ≠ \neq =B。设整个树的的异或值c,子树A、B的异或值分别为a,b。删除后A和B连向父节点的边,0节点为根的树、A节点为根的树、B节点为根的树的异或值分别为:
{ c ⊕ a , a ⊕ b , b a 是 b 祖先 c ⊕ b , a , b ⊕ a b 是 a 祖先 c ⊕ a ⊕ b , a , b o t h e r \begin{cases} c \oplus a ,a\oplus b, b & a是b祖先 \\ c \oplus b, a ,b \oplus a & b是a祖先 \\ c\oplus a \oplus b,a,b & other \\ \end{cases} ca,abbcba,bacab,a,bab祖先ba祖先other

一,DFS各子树的异或值,祖先后代关心,时间复杂度O(nn)。
二,枚举两个节点(边),时间复杂度O(nn)。

代码

核心代码

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 minimumScore(vector<int>& nums, vector<vector<int>>& edges) {
		m_c = nums.size();
		CNeiBo2 neiBo(m_c, edges, false);
		m_vXor.resize(m_c);
		m_vParent.assign(m_c, vector<bool>(m_c));
		vector<int> parent;
		DFS1(neiBo.m_vNeiB, 0, -1, nums, parent);
		int iRet = INT_MAX;
		int v[3];
		for (int i = 1; i < m_c; i++)
		{
			for (int j = 1; j < m_c; j++)
			{
				if (i == j)
				{
					continue;
				}	
				if (m_vParent[i][j])
				{
					v[0]=(m_vXor[0] ^  m_vXor[j]);
					v[1] = (m_vXor[i]);
					v[2] = (m_vXor[j] ^ m_vXor[i]);
				}
				else if(m_vParent[j][i])
				{
					v[0] = (m_vXor[0] ^ m_vXor[i]);
					v[1] = (m_vXor[i]^ m_vXor[j]);
					v[2] = (  m_vXor[j]);
				}
				else
				{
					v[0] = (m_vXor[0] ^ m_vXor[i] ^ m_vXor[j]);
					v[1] = (m_vXor[i]);
					v[2] = (m_vXor[j]);
				}
				sort(v, v+3);
				iRet = min(iRet, v[2] - v[0]);
			}
		}
		return iRet;
	}
	
	int DFS1(vector<vector<int>>& neiBo, int cur, int par, const vector<int>& nums, vector<int>& parent)
	{
		int ret = nums[cur];
		for (const auto& par1 : parent)
		{
			m_vParent[cur][par1] = true;
		}
		parent.emplace_back(cur);
		for (const auto& next : neiBo[cur])
		{
			if (next == par)
			{
				continue;
			}
			ret ^= DFS1(neiBo, next, cur, nums, parent);
		}
		parent.pop_back();
		return m_vXor[cur]=ret;
	}
	vector<int> m_vXor;
	vector<vector<bool>> m_vParent;
	int m_c;
};

测试用例

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()
{
	vector<int> nums;
	vector<vector<int>> edges;
	{
		Solution sln;
		nums = { 1,5,5,4,11 }, edges = { {0,1},{1,2},{1,3},{3,4} };
		auto res = sln.minimumScore(nums, edges);
		Assert(9, res);
	}
	
	{
		Solution sln;
		nums = { 5,5,2,4,4,2 }, edges = { {0,1},{1,2},{5,2},{4,3},{1,3} };
		auto res = sln.minimumScore(nums, edges);
		Assert(0, res);
	}
}

利用时间戳优化

已处理的节点中,时间戳大于cur的节点 是后代。两个变量分别记录:cur的时间戳,dfs(cur)结束时的时间戳。

2023年4月

class Solution {
public:
int minimumScore(vector& nums, vector<vector>& edges) {
m_c = nums.size();
m_vNeiB.resize(m_c);
m_vLeve.resize(m_c);
m_vXORSum.resize(m_c);
m_vInTime.resize(m_c);
m_vOutTime.resize(m_c);
m_nums = nums;
for (const auto& v : edges)
{
m_vNeiB[v[0]].emplace_back(v[1]);
m_vNeiB[v[1]].emplace_back(v[0]);
}
dfs(0, -1);
int iRet = INT_MAX;
std:vector v(3);
for (int i = 0; i < edges.size(); i++)
{
int iChild1 = (m_vLeve[edges[i][0]] > m_vLeve[edges[i][1]]) ? edges[i][0] : edges[i][1];
for (int j = i + 1; j < edges.size(); j++)
{
int iChild2 = (m_vLeve[edges[j][0]] > m_vLeve[edges[j][1]]) ? edges[j][0] : edges[j][1];
if (IsGrandParent(iChild1, iChild2))
{
v[0] = (m_vXORSum[iChild2] ^ m_vXORSum[iChild1]);
v[1] = (m_vXORSum[iChild1]);
v[2] = (m_vXORSum[0] ^ m_vXORSum[iChild1] ^ m_vXORSum[iChild2] ^ m_vXORSum[iChild1]);
}
else if (IsGrandParent(iChild2, iChild1))
{
v[0] = (m_vXORSum[iChild1] ^ m_vXORSum[iChild2]);
v[1] = (m_vXORSum[iChild2]);
v[2] = (m_vXORSum[0] ^ m_vXORSum[iChild1] ^ m_vXORSum[iChild2] ^ m_vXORSum[iChild2]);
}
else
{
v[0] = (m_vXORSum[iChild1]);
v[1] = (m_vXORSum[iChild2]);
v[2] = (m_vXORSum[0] ^ m_vXORSum[iChild1] ^ m_vXORSum[iChild2]);
}
const int iCurRet = *std::max_element(v.begin(), v.end()) - *std::min_element(v.begin(), v.end());
iRet = min(iRet, iCurRet);
}
}
return iRet;
}
bool IsGrandParent(int iNode1, int iIsGrandParent)
{
return (m_vInTime[iIsGrandParent] < m_vInTime[iNode1]) && (m_vOutTime[iIsGrandParent] >= m_vOutTime[iNode1]);
}
void dfs(int iCur, int iParent)
{
m_vInTime[iCur] = m_iTime++;
m_vLeve[iCur] = (-1 == iParent) ? 0 : m_vLeve[iParent]+1 ;
int iXorSum = m_nums[iCur];
for (const auto& next : m_vNeiB[iCur])
{
if (next == iParent)
{
continue;
}
dfs(next, iCur);
iXorSum ^= m_vXORSum[next];
}
m_vXORSum[iCur] = iXorSum;
m_vOutTime[iCur] = m_iTime;
}
int m_c;
vector<vector> m_vNeiB;
vector m_vLeve, m_vInTime, m_vOutTime;;
vector m_vXORSum;
vector m_nums;
int m_iTime = 1;
};

扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步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.coloradmin.cn/o/1490094.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vscode连接外部虚拟环境

如果vscode工程目录里面有一个超级大的虚拟环境文件夹&#xff0c;怎么说都不是一件优雅的事&#xff0c;因此我们希望这个虚拟环境在工程目录外部&#xff0c;我们开始&#xff1a; 1. 复制虚拟环境目录路径&#xff1a;E:\envs\test 2. 在vscode中打开文件夹&#xff0c;CT…

全球首个隐私计算一体机国际标准发布 蚂蚁摩斯参与编制

近日&#xff0c;IEEE 标准协会&#xff08;IEEE-SA&#xff09;正式发布并推行了全球首个隐私计算一体机国际标准《隐私计算一体机技术要求》&#xff08;IEEE 3156-2023&#xff09;。该标准由蚂蚁集团推动&#xff0c;中科院信息工程研究所、北京交通大学、中国信息通信研究…

第十六天-爬虫selenium库

目录 1.介绍 2.使用 selenium 1.安装 2.使用 1.测试打开网页&#xff0c;抓取雷速体育日职乙信息 2.通过xpath查找 3.输入文本框内容 send_keys 4.点击事件 click 5.获取网页源码&#xff1a; 6.获取cookies 7.seleniumt提供元素定位方式&#xff1a;8种 8.控制浏览…

第一弹:Flutter安装和配置

目标&#xff1a; 1&#xff09;配置Flutter开发环境 2&#xff09;创建第一个Flutter Demo项目 Flutter中文开发者网站&#xff1a; https://flutter.cn/ 一、配置Flutter开发环境 Flutter开发环境已经提供集成IDE开发环境&#xff0c;因此需要配置开发环境的时候&#xf…

Gitlab 安装部署

目录 1、Jenkins 结合 Gitlab 构建 CI/CD 环境 CI/CD 介绍 CI/CD 流程 Jenkins 简介 GitLab 简介 项目部署方式 CI系统的工作流程 2、搭建 GitLab 安装 GitLab 配置 GitLab 修改root密码 访问 GitLab 开机自启 3、使用 GitLab 管理 GitLab 关闭 GitLab 注册功能…

Git分支补充

我们在合并分支时并不总是一帆风顺&#xff0c;有些时候也会遇到“合并冲突”的问题。 下面我们来还原一下&#xff1a; 创建分支dev $ git checkout -b dev 切换到一个新分支 dev $ git branch * devmaster我们将 text.txt 内容改为 欢迎关注CSDNkeduo并将修改的内容提交到 d…

【Oracle Database】如何远程连接服务器、创建用户、从本地dmp导入表

C:\Users\test>imp test/123456ip/orcl:1521 fileE:\db.dmp tablestable1,table2Import: Release 11.2.0.3.0 - Production on 星期一 3月 4 12:59:09 2024Copyright (c) 1982, 2011, Oracle and/or its affiliates. All rights reserved.IMP-00058: 遇到 ORACLE 错误 1263…

EdgeX Foundry 安全模式安装部署

文章目录 一、安装准备1.官方文档2. 克隆服务器3.安装 Docker4.安装 docker-compose 二、安装部署1.docker-comepse2.启动 EdgeX Foundry3.访问 UI3.1. consul3.2. EdgeX Console EdgeX Foundry # EdgeX Foundryhttps://iothub.org.cn/docs/edgex/ https://iothub.org.cn/docs…

CUDA学习笔记02:测试程序hello world

参考资料 Win10下在VS2019中配置使用CUDA进行加速的C项目 &#xff08;配置.h文件&#xff0c;.dll以及.lib文件等&#xff09;_vs2019 cuda-CSDN博客 配置流程 1. 新建一个一般的项目 2. 项目建好后&#xff0c;在项目里添加.cu测试文件 测试的.cu文件命名为cuda_utils.cu&…

bert 相似度任务训练简单版本,faiss 寻找相似 topk

目录 任务 代码 train.py predit.py faiss 最相似的 topk 数 任务 使用 bert-base-chinese 训练相似度任务&#xff0c;参考&#xff1a;微调BERT模型实现相似性判断 - 知乎 参考他上面代码&#xff0c;他使用的是 BertForNextSentencePrediction 模型&#xff0c;Bert…

在idea中用模板骨架初始创建maven管理的web项目时没有src有关的目录的解决方案

一.问题如下 二.解决方法 首先关闭当前项目&#xff0c;接着修改全局设置&#xff0c;重新创建项目 在VM Options中添加"-DarchetypeCataloginternal"&#xff0c;点击ok保存 点击创建&#xff0c;如果创建成功没报错且有src&#xff0c;就ok了。 当然如果出现以下…

【C++】十大排序算法之 插入排序 希尔排序

本次介绍内容参考自&#xff1a;十大经典排序算法&#xff08;C实现&#xff09; - fengMisaka - 博客园 (cnblogs.com) 排序算法是《数据结构与算法》中最基本的算法之一。 十种常见排序算法可以分为两大类&#xff1a; 比较类排序&#xff1a;通过比较来决定元素间的相对次序…

大厂报价查询系统性能优化之道!

0 前言 机票查询系统&#xff0c;日均亿级流量&#xff0c;要求高吞吐&#xff0c;低延迟架构设计。提升缓存的效率以及实时计算模块长尾延迟&#xff0c;成为制约机票查询系统性能关键。本文介绍机票查询系统在缓存和实时计算两个领域的架构提升。 1 机票搜索服务概述 1.1 …

C++的类与对象(二)

目录 结构体内存对其规则 相关面试题 this指针 相关面试题 结构体内存对其规则 1、第一个成员在与结构体偏移量为0的地址处 2、其它成员变量要对齐到某个数字&#xff08;对齐数&#xff09;的整数倍的地址处 对齐数 编译器默认对齐数与该成员大小的较小值&#xff08;v…

学习记录12-单片机代码几种常见命名规则

良好的编程习惯&#xff0c;决定了今后代码的质量。 有很多人平时不注意自己的代码规范&#xff0c;函数和变量命命随心所欲&#xff0c;造成一个星期就不认识自己的代码&#xff0c;于是今天就来分享一点关于软件代码常见的几种命名规则。 匈牙利命名法 匈牙利命名法广泛应用…

RBAC实战

一、权限控制概述 1.1、访问控制目的 在实际的组织中&#xff0c;为了完成组织的业务工作&#xff0c;需要在组织内部设置不同的职位&#xff0c;职位既表示一种业务分工&#xff0c;又表示一种责任与权利。根据业务分工的需要&#xff0c;职位被划分给不同群体&#xff0c;各…

C++:Vector的模拟实现

创作不易&#xff0c;感谢三连 &#xff01;&#xff01; 一&#xff0c;前言 在学习string类的时候&#xff0c;我们可能会发现遍历的话下标访问特别香&#xff0c;比迭代器用的舒服&#xff0c;但是下标其实只能是支持连续的空间&#xff0c;他的使用是非常具有局限性的&am…

迷不迷糊?前后端、三层架构和MVC傻傻分不清

现在的项目都讲究前后端分离&#xff0c;那到底什么是前后端&#xff0c;前后端和以前的MVC以及三层架构啥关系呢&#xff1f;今天就这个问题展开一下&#xff0c;方面后面的学习&#xff0c;因为前面讲的jsp、servlet和javabean根据实例&#xff0c;基本上有一个框架的理解了&…

基于STC12C5A60S2系列1T 8051单片机的TM1638键盘数码管模块的按键扫描、数码管显示按键值、显示按键LED应用

基于STC12C5A60S2系列1T 8051单片机的TM1638键盘数码管模块的按键扫描、数码管显示按键值、显示按键LED应用 STC12C5A60S2系列1T 8051单片机管脚图STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式及配置STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式介绍TM1638键盘…

【如何在Docker中,修改已经挂载的卷(Volume)】

曾梦想执剑走天涯&#xff0c;我是程序猿【AK】 提示&#xff1a;添加投票&#xff01;&#xff01;&#xff01; 目录 简述概要知识图谱 简述概要 如何在Docker中&#xff0c;修改已经挂载的卷&#xff08;Volume&#xff09; 知识图谱 在Docker中&#xff0c;修改已经挂载…