【有序集合 有序映射 懒删除堆】 3510. 移除最小数对使数组有序 II|2608

本文涉及知识点

有序集合 有序映射 懒删除堆

3510. 移除最小数对使数组有序 II

给你一个数组 nums,你可以执行以下操作任意次数:

选择 相邻 元素对中 和最小 的一对。如果存在多个这样的对,选择最左边的一个。

用它们的和替换这对元素。

返回将数组变为 非递减 所需的 最小操作次数 。

如果一个数组中每个元素都大于或等于它前一个元素(如果存在的话),则称该数组为非递减。

示例 1:

输入: nums = [5,2,3,1]

输出: 2

解释:

复制代码
元素对 (3,1) 的和最小,为 4。替换后 nums = [5,2,4]。
元素对 (2,4) 的和为 6。替换后 nums = [5,6]。

数组 nums 在两次操作后变为非递减。

示例 2:

输入: nums = [1,2,2]

输出: 0

解释:

数组 nums 已经是非递减的。

提示:

1 < = n u m s . l e n g t h < = 10 5 1 <= nums.length <= 10^5 1<=nums.length<=105
− 10 9 < = n u m s [ i ] < = 10 9 -10^9 <= nums[i] <= 10^9 −109<=nums[i]<=109

有序集合 有序映射

cnt记录相邻逆序对数量。

有序集合, sSort[k]包括{nums[i]+nums[j],i,j}。

有序映射mNum[i] 记录nums[i]。注意 :mNum的key无需连续。故合并nums[i],nums[j],直接mNums[i] += mNums[j],删除mNums[j]。其它mNum无需修改。

令i的前一个下标是i1,j后面的下标是j1,则:

一,扣掉(i1,i)(i,j)(j,j1)的逆序对。

二,从sSort删除(i1,i)(i,j)(j,j1)。

三,修改mNum。

四,cnt 增加 mNum[i]的相邻逆序对。

五,sSort增加nums[i]的相邻项。

超时代码

倒数第二个例子超时。

cpp 复制代码
class Solution {
		public:
			int minimumPairRemoval(vector<int>& nums) {
				int cnt = 0;
				map<int, long long> mNum;
				set<tuple<long long, int, int>> sSort;
				auto AddIJ = [&](int i, int j) {
					assert(i < j);
					sSort.emplace(mNum[i] + mNum[j], i, j);
					cnt += mNum[i] > mNum[j];
				};
				for (int i = 0; i < nums.size();i++) {
					mNum[i] = nums[i];
					if (i)
					{
						AddIJ(i - 1, i);
					}
				}
				auto DelIJ = [&](int i, int j) {
					sSort.erase(make_tuple(mNum[i] + mNum[j], i, j));
					cnt -= mNum[i] > mNum[j];
				};
				auto Del = [&](int i, int j) {
					DelIJ(i, j);
					auto iti = mNum.find(i);
					auto itj = mNum.find(j);					
					if (mNum.begin() != iti) {
						auto it1 = prev(iti);
						DelIJ(it1->first, i);
					}
					auto it2 = next(itj);
					if (mNum.end() != it2) {						
						DelIJ(j, it2->first);
;					}
				};
				auto Add = [&](int i) {
					auto it = mNum.find(i);
					if (mNum.begin() != it) {
						AddIJ(prev(it)->first, i);
					}
					it = next(it);
					if (mNum.end() != it) {
						AddIJ(i, it->first);
;					}
				};
				while (cnt) {
					const auto [sum,i,j] = *sSort.begin();sSort.erase(sSort.begin());
					Del(i, j);
					mNum[i] += mNum[j];mNum.erase(mNum.find(j));
					Add(i);
				}
				return nums.size() - mNum.size();
			}
		};

懒删除

sSort的(i,j)如果失效一定是:i,j一个或全部被删除。

如果j和右邻被删除,mNum存在j。故要判断和是否一致。

cpp 复制代码
class Solution {
		public:
			int minimumPairRemoval(vector<int>& nums) {
				int cnt = 0;
				map<int, long long> mNum;
				typedef tuple<long long, int, int> T;
				priority_queue<T,vector<T>,greater<>> minHeap;
				auto AddIJ = [&](int i, int j) {
					assert(i < j);					
					cnt += mNum[i] > mNum[j];
				};
				for (int i = 0; i < nums.size();i++) {
					mNum[i] = nums[i];
					if (i)
					{
						minHeap.emplace(mNum[i-1] + mNum[i], i-1, i);
						AddIJ(i - 1, i);
					}
				}
				auto DelIJ = [&](int i, int j) {
					//sSort.erase(make_tuple(mNum[i] + mNum[j], i, j));
					cnt -= mNum[i] > mNum[j];
				};
				auto Del = [&](int i, int j) {
					DelIJ(i, j);
					auto iti = mNum.find(i);
					auto itj = mNum.find(j);					
					if (mNum.begin() != iti) {
						auto it1 = prev(iti);
						DelIJ(it1->first, i);
					}
					auto it2 = next(itj);
					if (mNum.end() != it2) {						
						DelIJ(j, it2->first);
;					}
				};
				auto Add = [&](int i) {
					auto it = mNum.find(i);
					if (mNum.begin() != it) {
						AddIJ(prev(it)->first, i);
					}
					it = next(it);
					if (mNum.end() != it) {
						AddIJ(i, it->first);
;					}
				};
				while (cnt) {
					const auto [sum, i, j] = minHeap.top();minHeap.pop();
					auto iti = mNum.find(i);
					auto itj = mNum.find(j);
					if ((mNum.end() == iti) || (mNum.end() == itj)) {continue;}//i,j都被删除
					if (sum != mNum[i] + mNum[j]) { continue; }
				/*	if (mNum.end() == iti) {
						if (mNum.begin() == itj) { continue; }
						const int i1 = prev(itj)->first;
						minHeap.emplace(mNum[i1] + mNum[j], i1, j);
						continue;
					}
					if (mNum.end() == itj) {
						auto it2 = next(iti);
						if (mNum.end() == it2) { continue; }
						const int j1 = it2->first;
						minHeap.emplace(mNum[i] + mNum[j1], i, j1);
						continue;
					}	*/	
					Del(i, j);
					mNum[i] += mNum[j];mNum.erase(mNum.find(j));
					Add(i);				
					if (mNum.begin() != iti) {
						const int i1 = prev(iti)->first;
						minHeap.emplace(mNum[i1] + mNum[i], i1, i);
					}
					auto it2 = next(iti);
					if (mNum.end() != it2) {
						const int j1 = it2->first;
						minHeap.emplace(mNum[i] + mNum[j1], i, j1);
					}
				}
				return nums.size() - mNum.size();
			}
		};

继续优化

cpp 复制代码
class Solution {
		public:
			int minimumPairRemoval(vector<int>& nums) {
				int cnt = 0;
				map<int, long long> mNum;
				typedef tuple<long long, int, int> T;
				priority_queue<T,vector<T>,greater<>> minHeap;
				for (int i = 0; i < nums.size();i++) {
					mNum[i] = nums[i];
					if (i)
					{
						minHeap.emplace(mNum[i-1] + mNum[i], i-1, i);
						cnt += nums[i - 1] > nums[i];
					}
				}		

				while (cnt) {
					const auto [sum, i, j] = minHeap.top();minHeap.pop();
					auto iti = mNum.find(i);
					auto itj = mNum.find(j);
					if ((mNum.end() == iti) || (mNum.end() == itj)) {continue;}//i,j都被删除
					if (sum != iti->second + itj->second) { continue; }	
					const auto it1 = (mNum.begin() != iti) ? prev(iti) : mNum.end();
					auto it2 = next(itj);
					cnt -= iti->second > itj->second;
					if (mNum.end() != it1) {
						cnt -= it1->second > iti->second;
					}
					if (mNum.end() != it2) {
						cnt -= itj->second > it2->second;
					}
					iti->second += itj->second;mNum.erase(itj);
					
					if (mNum.begin() != iti) {	
						auto it3 = prev(iti);
						minHeap.emplace(it3->second + iti->second, it3->first, iti->first);
						cnt += it3->second > iti->second;
					}	
					auto it4 = next(iti);
					if (mNum.end() != it4) {
						minHeap.emplace(iti->second+ it4->second,iti->first, it4->first);
						cnt += iti->second > it4->second;
					}
				}
				return nums.size() - mNum.size();
			}
		};

单元测试

cpp 复制代码
vector<int> nums;
		TEST_METHOD(TestMethod00)
		{
			nums = { 5,2,3,1 };
			auto res = Solution().minimumPairRemoval(nums);
			AssertEx(2, res);
		}
		TEST_METHOD(TestMethod01)
		{
			nums = {1,2,2 };
			auto res = Solution().minimumPairRemoval(nums);
			AssertEx(0, res);
		}
		TEST_METHOD(TestMethod02)
		{
			nums = { 1,1,4,4,2,-4,-1 };
			auto res = Solution().minimumPairRemoval(nums);
			AssertEx(5, res);
		}
		TEST_METHOD(TestMethod03)
		{
			nums = { -2,1,2,-1,-1,-2,-2,-1,-1,1,1 };
			auto res = Solution().minimumPairRemoval(nums);
			AssertEx(10, 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++**实现。

相关推荐
ajassi20005 小时前
开源 C++ QT Widget 开发(八)网络--Http文件下载
网络·c++·开源
卡尔曼的BD SLAMer6 小时前
计算机视觉与深度学习 | 基于深度学习的图像特征提取与匹配算法综述及MATLAB实现
人工智能·深度学习·算法·计算机视觉·matlab
Tipriest_6 小时前
C++ 中 ::(作用域解析运算符)的用途
开发语言·c++·作用域解析
0wioiw07 小时前
算法(③二叉树)
算法
Tipriest_7 小时前
求一个整数x的平方根到指定精度[C++][Python]
开发语言·c++·python
WHS-_-20227 小时前
Carrier Aggregation Enabled MIMO-OFDM Integrated Sensing and Communication
算法
何妨重温wdys8 小时前
贪心算法解决活动选择问题:最多不重叠活动数量求解
算法·贪心算法
cheniie9 小时前
网格纹理采样算法
算法
John_ToDebug9 小时前
从源码看浏览器弹窗消息机制:SetDefaultView 的创建、消息转发与本地/在线页通用实践
开发语言·c++·chrome