【对顶队列】【中位数贪心】【前缀和】100227. 拾起 K 个 1 需要的最少行动次数

news2025/2/22 12:37:49

本文涉及知识点

C++算法:前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频
对顶队列(栈) 分类讨论

LeetCode100227. 拾起 K 个 1 需要的最少行动次数

给你一个下标从 0 开始的二进制数组 nums,其长度为 n ;另给你一个 正整数 k 以及一个 非负整数 maxChanges 。
灵茶山艾府在玩一个游戏,游戏的目标是让灵茶山艾府使用 最少 数量的 行动 次数从 nums 中拾起 k 个 1 。游戏开始时,灵茶山艾府可以选择数组 [0, n - 1] 范围内的任何索引index 站立。如果 nums[index] == 1 ,灵茶山艾府就会拾起一个 1 ,并且 nums[index] 变成0(这 不算 作一次行动)。之后,灵茶山艾府可以执行 任意数量 的 行动(包括零次),在每次行动中灵茶山艾府必须 恰好 执行以下动作之一:
选择任意一个下标 j != index 且满足 nums[j] == 0 ,然后将 nums[j] 设置为 1 。这个动作最多可以执行 maxChanges 次。
选择任意两个相邻的下标 x 和 y(|x - y| == 1)且满足 nums[x] == 1, nums[y] == 0 ,然后交换它们的值(将 nums[y] = 1 和 nums[x] = 0)。如果 y == index,在这次行动后灵茶山艾府拾起一个 1 ,并且 nums[y] 变成 0 。
返回灵茶山艾府拾起 恰好 k 个 1 所需的 最少 行动次数。
示例 1:
输入:nums = [1,1,0,0,0,1,1,0,0,1], k = 3, maxChanges = 1
输出:3
解释:如果游戏开始时灵茶山艾府在 index == 1 的位置上,按照以下步骤执行每个动作,他可以利用 3 次行动拾取 3 个 1 :
游戏开始时灵茶山艾府拾取了一个 1 ,nums[1] 变成了 0。此时 nums 变为 [1,0,0,0,0,1,1,0,0,1] 。
选择 j == 2 并执行第一种类型的动作。nums 变为 [1,0,1,0,0,1,1,0,0,1]
选择 x == 2 和 y == 1 ,并执行第二种类型的动作。nums 变为 [1,1,0,0,0,1,1,0,0,1] 。由于 y == index,灵茶山艾府拾取了一个 1 ,nums 变为 [1,0,0,0,0,1,1,0,0,1] 。
选择 x == 0 和 y == 1 ,并执行第二种类型的动作。nums 变为 [0,1,0,0,0,1,1,0,0,1] 。由于 y == index,灵茶山艾府拾取了一个 1 ,nums 变为 [0,0,0,0,0,1,1,0,0,1] 。
请注意,灵茶山艾府也可能执行其他的 3 次行动序列达成拾取 3 个 1 。

示例 2:

输入:nums = [0,0,0,0], k = 2, maxChanges = 3

输出:4

解释:如果游戏开始时灵茶山艾府在 index == 0 的位置上,按照以下步骤执行每个动作,他可以利用 4 次行动拾取 2 个 1 :

选择 j == 1 并执行第一种类型的动作。nums 变为 [0,1,0,0] 。
选择 x == 1 和 y == 0 ,并执行第二种类型的动作。nums 变为 [1,0,0,0] 。由于 y == index,灵茶山艾府拾起了一个 1 ,nums 变为 [0,0,0,0] 。
再次选择 j == 1 并执行第一种类型的动作。nums 变为 [0,1,0,0] 。
再次选择 x == 1 和 y == 0 ,并执行第二种类型的动作。nums 变为 [1,0,0,0] 。由于y == index,灵茶山艾府拾起了一个 1 ,nums 变为 [0,0,0,0] 。
提示:
2 <= n <= 105
0 <= nums[i] <= 1
1 <= k <= 105
0 <= maxChanges <= 105
maxChanges + sum(nums) >= k

分类讨论

1,消耗0行动次数。nums[index]为1。
2,消耗1行动次数,nums[index-1]或nums[index+1]为1。
3,消耗2行动次数,一转换次数。nums[index-1]或nums[index+1]设置为1,再移动。
4,消耗2或更多行动次数。将其它的1移动过来。    ⟺    \iff 计算距离index 最近的m个一。

枚举index,时间复杂度O(n)。前三种分类,时间复杂度都是O(1),如何将第四类操作的时间复杂度将为O(1)。
如果有转换次数,一定用分类三,不用分类四。故需要讨论分类四时,maxChanges 次数已经用完,故m = k - maxChange。 用对顶队列分别记录下标小于index 的1下标,下标大于等于index的1下标。

程序流程

一,用队列升序收集1的下标。
二,利用对顶队列计算 距离index最近的m个1。
三,分别处理情况一到四。注意:拾取次数不能超过k。m个1要扣除分类一和分类二的1。

对顶队列预处理最近m个1

一,计算vPre[0]。
二,如果队列不发生变化,vPre[i2] = vPre[i2 - 1] + que1.size() - que2.size();
三,如果que2移到que1,触发条件que2.front() < i2。由于i2一次加1,所以que2.front()此时一定等于i2-1。应该是1,误算成-1。故+2。 que1入队,que2出队。
三,枚举i2是,同时 枚举que1Index。index1.front()一定> i2,否则ndex1.front() < i2-1,应该进入que1。index是升序,故que1中的下标都小于ndex1.front(),故ndex1.front()比que1中的数距离i2-1都近。一定会淘汰que1中的数据。
因为ndex1.front()大于que2中的下标,故不会淘汰que2中的数。
que1中的数,越小越容易淘汰,故从队首开始淘汰。

代码

核心代码

class Solution {
public:
	long long minimumMoves(const vector<int>& nums, const int k, const int maxChanges) {
		m_c = nums.size();
		queue<int> que1Index;
		for (int i = 0; i < m_c; i++)
		{
			if (nums[i])
			{
				que1Index.emplace(i);
			}
		}
		vector<long long> vPre(nums.size());//距离 nums[i]最近的iNum个1的距离
		const int i1Num = k - maxChanges;		
		queue<int> que1, que2;
		while(que1Index.size() && (que2.size() < i1Num))
		{//计算vPre[0]
			que2.emplace(que1Index.front());
			vPre[0] += que1Index.front();
			que1Index.pop();
		}
		for (int i2 = 1; i2 < m_c; i2++)
		{
			vPre[i2] = vPre[i2 - 1] + que1.size() - que2.size();
			if (que2.size() && (que2.front() < i2))
			{//队列二向队列一移动
				que1.emplace(que2.front());
				que2.pop();
				vPre[i2] += 1 + 1;
			}
			while (que1.size() && que1Index.size() && (i2 - que1.front() > que1Index.front() - i2))
			{
				vPre[i2] -= (i2 - que1.front());
				vPre[i2] += que1Index.front() - i2;
				que2.emplace(que1Index.front());
				que1.pop();
				que1Index.pop();
			}
		}
		long long llRet = LLONG_MAX;;
		for (int i = 0; i < nums.size(); i++)
		{
			int iNeed = k - nums[i];	
			long long llDo = 0;
			if ((iNeed > 0) && (i > 0) && nums[i - 1])
			{
				iNeed--; llDo++;
			}
			if ((iNeed > 0) && (i+1 < nums.size()) && nums[i + 1])
			{
				iNeed--; llDo++;
			}
			const int iNeiBo = llDo;
			const int iChange = min(maxChanges, iNeed);
			iNeed -= iChange;
			llDo += 2 * iChange;
			if (iNeed > 0)
			{
				llDo += vPre[i] - iNeiBo;
			}
			llRet = min(llRet, llDo);
		}
		return llRet;
	}
	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;
	 int k, maxChanges;
	 {
		 nums = { 1,0,1,0,1 }, k = 3, maxChanges = 0;
		 auto res = Solution().minimumMoves(nums, k, maxChanges);
		 Assert(4, res);
	 }
	{
		 nums = { 1, 1, 0, 0, 0, 1, 1, 0, 0, 1 }, k = 3, maxChanges = 1;
		 auto res = Solution().minimumMoves(nums, k, maxChanges);
		Assert(3, res);
	}
	{
		nums = { 0, 0, 0, 0 }, k = 2, maxChanges = 3;
		auto res = Solution().minimumMoves(nums, k, maxChanges);
		Assert(4, res);
	}
}

优化

如果有情况四,情况一二三,可以统一。

class Solution {
public:
	long long minimumMoves(const vector<int>& nums, const int k, const int maxChanges) {
		m_c = nums.size();
		queue<int> que1Index;
		for (int i = 0; i < m_c; i++)
		{
			if (nums[i])
			{
				que1Index.emplace(i);
			}
		}
		vector<long long> vPre(nums.size());//距离 nums[i]最近的iNum个1的距离
		const int i1Num = k - maxChanges;		
		queue<int> que1, que2;
		while(que1Index.size() && (que2.size() < i1Num))
		{//计算vPre[0]
			que2.emplace(que1Index.front());
			vPre[0] += que1Index.front();
			que1Index.pop();
		}
		for (int i2 = 1; i2 < m_c; i2++)
		{
			vPre[i2] = vPre[i2 - 1] + que1.size() - que2.size();
			if (que2.size() && (que2.front() < i2))
			{//队列二向队列一移动
				que1.emplace(que2.front());
				que2.pop();
				vPre[i2] += 1 + 1;
			}
			while (que1.size() && que1Index.size() && (i2 - que1.front() > que1Index.front() - i2))
			{
				vPre[i2] -= (i2 - que1.front());
				vPre[i2] += que1Index.front() - i2;
				que2.emplace(que1Index.front());
				que1.pop();
				que1Index.pop();
			}
		}
		long long llRet = LLONG_MAX;;
		for (int i = 0; i < nums.size(); i++)
		{
			int iNeed = k - nums[i];	
			long long llDo = 0;
			if ((iNeed > 0) && (i > 0) && nums[i - 1])
			{
				iNeed--; llDo++;
			}
			if ((iNeed > 0) && (i+1 < nums.size()) && nums[i + 1])
			{
				iNeed--; llDo++;
			}
			if (maxChanges >= iNeed)
			{
				llDo += 2 * iNeed;
			}
			else
			{
				llDo = vPre[i] + maxChanges * 2;
			}
			llRet = min(llRet, llDo);
		}
		return llRet;
	}
	int m_c;
};

中位数贪心

如果某个数距离m个数距离最短,那么它一定是正中间的数。也就是情况四:左边m/2个1,右边m-1-m/2个1。用前缀和计算。
nums[index]不会为0。
一,000。根据中位数贪心,相比换到这m个数的中心。情况一二四一定不会有优势。情况一二也没优先。
二,100,同上。
三,101。假定101左边有m1个1,右边有m2个1。如果m1 <= m2,移到101 ,否则移到101
除101外的数距离全部变短。101的距离从:1,1变成 0,2 持平。

如何计算左边m1个和

vPre[i]记录前i个1到0的距离。
故第j个1前面的m1个。m1 × \times × index[j]- (vPre[j]-vPre[j-m1])
故第j个1后面的m2个 vPre[j+1+m2] - vPre[j+1] - m2 × \times ×index[j]

特例

全部是0。

代码

class Solution {
public:
	long long minimumMoves(const vector<int>& nums, const int k, const int maxChanges) {
		m_c = nums.size();
		vector<int> v1Index;
		for (int i = 0; i < m_c; i++)
		{
			if (nums[i])
			{
				v1Index.emplace_back(i);
			}
		}
		if (v1Index.empty())
		{
			return k * 2;
		}
		vector<long long> vPre = { 0 };
		for (const auto& n : v1Index)
		{
			vPre.emplace_back(vPre.back() + n);
		}
		long long llRet = LLONG_MAX;;
		for (int j = 0 ; j < v1Index.size();j++ )
		{
			const int& i = v1Index[j];
			int iNeed = k - nums[i];	
			long long llDo = 0;
			if ((iNeed > 0) && (i > 0) && nums[i - 1])
			{
				iNeed--; llDo++;
			}
			if ((iNeed > 0) && (i+1 < nums.size()) && nums[i + 1])
			{
				iNeed--; llDo++;
			}
			if (maxChanges >= iNeed)
			{
				llDo += 2 * iNeed;
			}
			else
			{
				const int m = k - maxChanges;
				const long long m1 = m / 2;
				const long long m2 = m - m1 - 1;
				if ((j - m1 < 0) || (j + 1 + m2 >= vPre.size()))
				{
					continue;
				}
				const long long llLeft = m1 * i - (vPre[j] - vPre[j - m1]);
				const long long llRight = vPre[j + 1 + m2] - vPre[j + 1] - m2 * i;
				llDo = llLeft + llRight + maxChanges * 2;
			}
			llRet = min(llRet, llDo);
		}
		return llRet;
	}
	int m_c;
};

扩展阅读

视频课程

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

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

相关文章

【LabVIEW FPGA入门】浮点数类型支持

如今&#xff0c;使用浮点运算来设计嵌入式系统的需求变得越来越普遍。随着 FPGA 因其固有的大规模并行性而在浮点性能方面继续超越微处理器&#xff0c;这种情况正在加剧。线性代数和数字信号处理 (DSP) 等高级算法可以受益于浮点数据类型的高动态范围精度。LabVIEW FPGA 通过…

Vue项目的搭建

Node.js 下载 Node.js — Download (nodejs.org)https://nodejs.org/en/download/ 安装 测试 winR->cmd执行 node -v配置 在安装目录下创建两个子文件夹node_cache和node_global,我的就是 D:\nodejs\node_cache D:\nodejs\node_global 在node_global文件下再创建一个…

视频基础知识(一) 视频编码 | H.26X 系列 | MPEG 系列 | H.265

文章目录 一、视频编码二、 H.26X 系列1、H.2612、H.2633、H.2643.1 I帧3.2 P帧3.3 B帧 4、H.265 三、 MPEG 系列1、MPEG-12、MPEG-23、MPEG-44、MPEG-7 &#x1f680; 个人简介&#xff1a;CSDN「博客新星」TOP 10 &#xff0c; C/C 领域新星创作者&#x1f49f; 作 者&…

【HTML】HTML表单8.2(表单标签2)

目录 接上期&#xff0c;大致实现效果 文章简要 注释&#xff1a;这一次介绍的很多效果需要后期与服务器配合&#xff0c;但我们这里先只介绍效果 ①提交按钮 ②获取验证码 ③上传文件 ④还原所有表单内容 ⑤下拉表单 ⑥文字域 接上期&#xff0c;大致实现效果 文章简要 注…

OpenCV-Java 开发简介

返回目录&#xff1a;OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇&#xff1a;如何在“Microsoft Visual Studio”中使用OpenCV编译应用程序 下一篇&#xff1a;如何将OpenCV Java 与Eclipse结合使用 警告&#xff1a; 本教程可能包含过时的信息。 …

伺服电机编码器的分辨率指得是什么?

伺服电机编码器的分辨率是伺服电机编码器的重要参数。 一般来说&#xff0c;具体的伺服电机编码器型号可以找到对应的分辨率值。 伺服电机编码器的分辨率和精度不同&#xff0c;但也有一定的关系。 伺服电机编码器的分辨率是多少&#xff1f; 1、伺服编码器&#xff08;同步伺…

【JS】html字符转义

需求 将html转为字符串将html字符串转义&#xff0c;比如<div>转为<div> 码 /*** html标签字符转义* param {Stirng} str 要转换的html字符* returns String 返回转义的html字符串*/ const elToStr str > str.replaceAll(<, <).replaceAll(>, >)…

Github: Github actions自动化工作原理与多workflow创建和部署

Github actions 1 &#xff09;概述 Github Actions 是Github官方推出的 CI/CD 解决方案 https://docs.githu.com/en/actions 优点 自动发布流程可减少发布过程中手动操作成本&#xff0c;大幅提升ci/cd效率&#xff0c;快速实现项目发布上线 缺点 存在较高的技术门槛需要利用…

论文阅读:Face Deblurring using Dual Camera Fusion on Mobile Phones

今天介绍一篇发表在 ACM SIGGRAPH 上的文章&#xff0c;是用手机的双摄系统来做人脸去模糊的工作。这也是谷歌计算摄影研究组的工作。 快速运动物体的运动模糊在摄影中是一个一直以来的难题&#xff0c;在手机摄影中也是非常常见的问题&#xff0c;尤其在光照不足&#xff0c;…

【FPGA/IC】什么是模块化设计?

什么是模块化设计 FPGA/IC设计中根据模块层次的不同有两种基本的设计方法&#xff1a; 自下而上方法对设计进行逐次划分的过程是从基本单元出发的&#xff0c;设计树最末枝上的单元是已经设计好的基本单元&#xff0c;或者其他项目开发好的单元或者IP。该方法先对底层的功能块…

8-图像缩放

其实&#xff0c;就是开辟一个zoomwidth&#xff0c;zoomheight的内存&#xff0c;再分别赋值即可。 void CDib::Scale(float xZoom, float yZoom) { //指向原图像指针 LPBYTE p_data GetData(); //指向原像素的指针 LPBYTE lpSrc; //指向缩放图像对应像素的指针 LPBYTE lpDs…

RTC协议与算法基础 - RTP/RTCP

首先&#xff0c;需要说明下&#xff0c;webrtc的核心音视频传输是通过RTP/RTCP协议实现的&#xff0c;源码位于src/modules/rtp_rtcp目录下&#xff1a; 下面让我们对相关的内容基础进行简要分析与说明&#xff1a; 一、TCP与UDP协议 1.1、TCP协议 TCP为了实现数据传输的可…

基于grafana+elk等开源组件的 云服务监控大屏架构

本套大屏,在某云服务大规模测试环境,良好运行3年. 本文主要展示这套监控大屏的逻辑架构.不做具体操作与配置的解释. 监控主要分为三部分: 数据展示部分数据存储数据采集 1. 数据展示 数据展示方面主要使用grafana 2. 数据存储 根据数据种类和特性和用途的不同,本套监控采用…

专业120+总400+北京理工大学826信号处理导论考研经验北理工电子信息与通信工程,真题,大纲,参考书。

**今年专业课826信号处理导论&#xff08;信号系统和数字信号处理&#xff09;120&#xff0c;总分400&#xff0c;应群里同学需要&#xff0c;自己总结一下去年的复习经历&#xff0c;希望对大家复习有帮助。**专业课&#xff1a; 北京理工大学专业826是两门合一&#xff0c;…

应用开发平台集成表单设计器系列之4——表单构造器深度了解

背景 平台需要实现自定义表单功能&#xff0c;作为低代码开发的一部分&#xff0c;通过技术预研和技术选型&#xff0c;选择form-create和form-create-designer这两个组件进行集成作为实现方案。通过深入了解和技术验证&#xff0c;确认了组件的功能能满足需求&#xff0c;具备…

Tensorflow笔记(二):激活函数、优化器等、神经网络模型实现(商品销量预测)

import tensorflow as tf import numpy as np from tqdm import tqdm# ----------------------------- tensor常用函数2 ----------------------------------- a tf.constant([1, 2, 3, 1, 2]) b tf.constant([0, 1, 3, 4, 5]) c tf.where(tf.greater(a, b), a, b) # 若a&g…

6语言交易所/多语言交易所php源码/微盘PHP源码

6语言交易所PHP源码&#xff0c;简单测试了一下&#xff0c;功能基本都是正常的。 由于是在本地测试的运行环境的问题&#xff0c;K线接口有点问题&#xff0c;应该在正式环境下是OK的。 源码下载地址&#xff1a;6语言交易所/多语言交易所php源码/微盘PHP源码.zip 程序截图…

YOLOv5改进 | 图像去雾 | 利用图像去雾网络UnfogNet辅助YOLOv5进行图像去雾检测(全网独家首发)

一、本文介绍 本文给大家带来的改进机制是利用UnfogNet超轻量化图像去雾网络,我将该网络结合YOLOv5针对图像进行去雾检测(也适用于一些模糊场景),我将该网络结构和YOLOv5的网络进行结合同时该网络的结构的参数量非常的小,我们将其添加到模型里增加的计算量和参数量基本可…

PC电脑如何使用HDMI连接小米电视当显示屏

使用HDMI连接好当时和电脑&#xff0c;HDMI2.0会更清晰&#xff1b;小米电视会自动弹窗提示你有HDMI 接口连接&#xff0c;或者你进入信号源进行选择即可&#xff1b;需要平时我们电脑的显示器正常连接&#xff0c;然后按 win p &#xff0c;选择 扩展 屏幕&#xff1b; 进入设…

Spring MVC文件上传配置

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 文件上传 Spring MVC文件上传基于Servlet 3.0实现&#xff1b;示例代码如下&#xff1a; Overrideprotected void customizeRegistration(ServletRegistration.Dynamic reg…