【位运算 贪心】2835. 使子序列的和等于目标的最少操作次数

2024-04-16 07:36

本文主要是介绍【位运算 贪心】2835. 使子序列的和等于目标的最少操作次数,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

算法可以发掘本质,如:
一,若干师傅和徒弟互有好感,有好感的师徒可以结对学习。师傅和徒弟都只能参加一个对子。如何让对子最多。
二,有无限多1X2和2X1的骨牌,某个棋盘若干格子坏了,如何在没有坏的格子放足够多骨牌。
三,某个单色图,1表示前前景,0表示后景色。每次操作可以将一个1,变成0。如何在最少得操作情况下,使得没有两个1相邻(四连通)。
四,若干路人,有些人是熟人,如何选出最多的人参加实验。为了避免熟人影响实验的效果,参加的人不能是熟人。
一二是二分图的最大匹配,三是二分图的最小点覆盖,四是二分图最大独立集。 而这三者是等效问题。

本文涉及知识点

位运算 贪心

LeetCode 2835. 使子序列的和等于目标的最少操作次数

给你一个下标从 0 开始的数组 nums ,它包含 非负 整数,且全部为 2 的幂,同时给你一个整数 target 。
一次操作中,你必须对数组做以下修改:
选择数组中一个元素 nums[i] ,满足 nums[i] > 1 。
将 nums[i] 从数组中删除。
在 nums 的 末尾 添加 两个 数,值都为 nums[i] / 2 。
你的目标是让 nums 的一个 子序列 的元素和等于 target ,请你返回达成这一目标的 最少操作次数 。如果无法得到这样的子序列,请你返回 -1 。

数组中一个 子序列 是通过删除原数组中一些元素,并且不改变剩余元素顺序得到的剩余数组。

示例 1:

输入:nums = [1,2,8], target = 7
输出:1
解释:第一次操作中,我们选择元素 nums[2] 。数组变为 nums = [1,2,4,4] 。
这时候,nums 包含子序列 [1,2,4] ,和为 7 。
无法通过更少的操作得到和为 7 的子序列。
示例 2:

输入:nums = [1,32,1,2], target = 12
输出:2
解释:第一次操作中,我们选择元素 nums[1] 。数组变为 nums = [1,1,2,16,16] 。
第二次操作中,我们选择元素 nums[3] 。数组变为 nums = [1,1,2,16,8,8] 。
这时候,nums 包含子序列 [1,1,2,8] ,和为 12 。
无法通过更少的操作得到和为 12 的子序列。
示例 3:

输入:nums = [1,32,1], target = 35
输出:-1
解释:无法得到和为 35 的子序列。

提示:

1 <= nums.length <= 1000
1 <= nums[i] <= 230
nums 只包含非负整数,且均为 2 的幂。
1 <= target < 231

位运算

target可以拆分成 2i1+2i2+ ⋯ \cdots + 2 in
性质一:如果2j1+2j2 … \dots +2jm >= 2i 且 j1到jm都小于等于i。
则一定可以从 j1,j2 ⋯ \cdots jm 中选择若干数,使得其和等于2i
证明
i = 0 时 。 20=20.
x>=1,如果i=x,性质一成立,则i=x+1,性质一也成立。
由于左式 >= 2x+1 > 2x 故左式可以抽取s = 2x
左式 - S >= 2x,故还可以抽取S2 = 2x
S+S2和在一起,就是2x+1
性质二
令集合 T = {2j1,2j2 ⋯ \cdots , 2jm} , T 中可能有重复的数据。
令集合S ={ 2i1,2i2+ ⋯ \cdots , 2in },其中i1<i2 < ⋯ \cdots in S的和等于target
∀ i ( i ∈ i 1 , i 2 , ⋯ , i n ) t a r g e t i = ∑ x < = 2 i , x ∈ S T i = ∑ x < = 2 i x , x ∈ T \forall i(i \in{i1,i2,\cdots,in}) \quad targeti = \sum_{x <= 2^i },x\in S \quad Ti=\sum_{x <= 2^i }x,x\in T i(ii1,i2,,in)targeti=x<=2ixSTi=x<=2ix,xT
如果Ti大于等于targeti ⟺ \iff 本题
情况一:target只有一项,就是性质一。
情况二:如果target有x项成立,则x+1项也成立。移除x项后,就成了性质一。

解法
通过i从低位到高位枚举target,其和记录到:iNeed。
cur = 1 << i 。
nums中小于等于cur的加到llHas中。
如果 llHas < iNeed, 则拆分nums中的最小元素next到cur,如果无元素可拆分,则返回-1。
拆分后:一个cur加到llHas, next/2 next/4 … \dots cur 加到setNum。

本解法用的多键集合,其实用大根堆 更简洁。

代码

核心代码

class Solution {
public:int minOperations(vector<int>& nums, int target) {std::multiset<int> setNum(nums.begin(), nums.end());int iRet = 0;long long llHas	= 0;int iNeed = 0;for (int i = 0; i <= 30; i++) {const int cur = 1 << i;while (setNum.size() && (*setNum.begin() <= cur)) {llHas += *setNum.begin();setNum.erase(setNum.begin());}if (cur & target) {iNeed += cur;}while (llHas < iNeed) {auto it = setNum.lower_bound(cur);if (setNum.end() == it) { return -1; }int next = *it;setNum.erase(it);				while (cur != next) {next /= 2;setNum.emplace(next);iRet++;}llHas += cur;}	}return iRet;}
};

测试用例

template<class T>
void Assert(const T& t1, const T& 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; int target;{Solution sln;nums = { 1, 2, 8 }, target = 7;auto res = sln.minOperations(nums, target);Assert(1, res);}{Solution sln;nums = { 1, 32, 1, 2 }, target = 12;auto res = sln.minOperations(nums, target);Assert(2, res);}{Solution sln;nums = { 1,32,1 }, target = 35;auto res = sln.minOperations(nums, target);Assert(-1, res);}}

扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步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++**实现。

这篇关于【位运算 贪心】2835. 使子序列的和等于目标的最少操作次数的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/908169

相关文章

Python调用Orator ORM进行数据库操作

《Python调用OratorORM进行数据库操作》OratorORM是一个功能丰富且灵活的PythonORM库,旨在简化数据库操作,它支持多种数据库并提供了简洁且直观的API,下面我们就... 目录Orator ORM 主要特点安装使用示例总结Orator ORM 是一个功能丰富且灵活的 python O

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型的操作流程

《0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeekR1模型的操作流程》DeepSeekR1模型凭借其强大的自然语言处理能力,在未来具有广阔的应用前景,有望在多个领域发... 目录0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型,3步搞定一个应

最长公共子序列问题的深度分析与Java实现方式

《最长公共子序列问题的深度分析与Java实现方式》本文详细介绍了最长公共子序列(LCS)问题,包括其概念、暴力解法、动态规划解法,并提供了Java代码实现,暴力解法虽然简单,但在大数据处理中效率较低,... 目录最长公共子序列问题概述问题理解与示例分析暴力解法思路与示例代码动态规划解法DP 表的构建与意义动

关于最长递增子序列问题概述

《关于最长递增子序列问题概述》本文详细介绍了最长递增子序列问题的定义及两种优化解法:贪心+二分查找和动态规划+状态压缩,贪心+二分查找时间复杂度为O(nlogn),通过维护一个有序的“尾巴”数组来高效... 一、最长递增子序列问题概述1. 问题定义给定一个整数序列,例如 nums = [10, 9, 2

轻松上手MYSQL之JSON函数实现高效数据查询与操作

《轻松上手MYSQL之JSON函数实现高效数据查询与操作》:本文主要介绍轻松上手MYSQL之JSON函数实现高效数据查询与操作的相关资料,MySQL提供了多个JSON函数,用于处理和查询JSON数... 目录一、jsON_EXTRACT 提取指定数据二、JSON_UNQUOTE 取消双引号三、JSON_KE

C++实现封装的顺序表的操作与实践

《C++实现封装的顺序表的操作与实践》在程序设计中,顺序表是一种常见的线性数据结构,通常用于存储具有固定顺序的元素,与链表不同,顺序表中的元素是连续存储的,因此访问速度较快,但插入和删除操作的效率可能... 目录一、顺序表的基本概念二、顺序表类的设计1. 顺序表类的成员变量2. 构造函数和析构函数三、顺序表

使用C++实现单链表的操作与实践

《使用C++实现单链表的操作与实践》在程序设计中,链表是一种常见的数据结构,特别是在动态数据管理、频繁插入和删除元素的场景中,链表相比于数组,具有更高的灵活性和高效性,尤其是在需要频繁修改数据结构的应... 目录一、单链表的基本概念二、单链表类的设计1. 节点的定义2. 链表的类定义三、单链表的操作实现四、

Python利用自带模块实现屏幕像素高效操作

《Python利用自带模块实现屏幕像素高效操作》这篇文章主要为大家详细介绍了Python如何利用自带模块实现屏幕像素高效操作,文中的示例代码讲解详,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1、获取屏幕放缩比例2、获取屏幕指定坐标处像素颜色3、一个简单的使用案例4、总结1、获取屏幕放缩比例from

通过prometheus监控Tomcat运行状态的操作流程

《通过prometheus监控Tomcat运行状态的操作流程》文章介绍了如何安装和配置Tomcat,并使用Prometheus和TomcatExporter来监控Tomcat的运行状态,文章详细讲解了... 目录Tomcat安装配置以及prometheus监控Tomcat一. 安装并配置tomcat1、安装