本文涉及知识点
有序集合 有序映射 懒删除堆
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++**实现。