【有序集合 有序映射 懒删除堆】 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<=nums.length<=1051 <= nums.length <= 10^51<=nums.length<=105
−109<=nums[i]<=109-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]的相邻项。
超时代码
倒数第二个例子超时。
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。故要判断和是否一致。
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();}};
继续优化
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();}};
单元测试
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++**实现。