【堆 优先队列】2163. 删除元素后和的最小差值|2225

本文涉及知识带你

C++堆(优先队列)

LeetCode2163. 删除元素后和的最小差值

给你一个下标从 0 开始的整数数组 nums ,它包含 3 * n 个元素。
你可以从 nums 中删除 恰好 n 个元素,剩下的 2 * n 个元素将会被分成两个 相同大小 的部分。
前面 n 个元素属于第一部分,它们的和记为 sumfirst 。
后面 n 个元素属于第二部分,它们的和记为 sumsecond 。
两部分和的 差值 记为 sumfirst - sumsecond 。
比方说,sumfirst = 3 且 sumsecond = 2 ,它们的差值为 1 。
再比方,sumfirst = 2 且 sumsecond = 3 ,它们的差值为 -1 。
请你返回删除 n 个元素之后,剩下两部分和的 差值的最小值 是多少。
示例 1:
输入:nums = [3,1,2]
输出:-1
解释:nums 有 3 个元素,所以 n = 1 。
所以我们需要从 nums 中删除 1 个元素,并将剩下的元素分成两部分。

  • 如果我们删除 nums[0] = 3 ,数组变为 [1,2] 。两部分和的差值为 1 - 2 = -1 。
  • 如果我们删除 nums[1] = 1 ,数组变为 [3,2] 。两部分和的差值为 3 - 2 = 1 。
  • 如果我们删除 nums[2] = 2 ,数组变为 [3,1] 。两部分和的差值为 3 - 1 = 2 。
    两部分和的最小差值为 min(-1,1,2) = -1 。
    示例 2:
    输入:nums = [7,9,5,8,1,3]
    输出:1
    解释:n = 2 。所以我们需要删除 2 个元素,并将剩下元素分为 2 部分。
    如果我们删除元素 nums[2] = 5 和 nums[3] = 8 ,剩下元素为 [7,9,1,3] 。和的差值为 (7+9) - (1+3) = 12 。
    为了得到最小差值,我们应该删除 nums[1] = 9 和 nums[4] = 1 ,剩下的元素为 [7,5,8,3] 。和的差值为 (7+5) - (8+3) = 1 。
    观察可知,最优答案为 1 。
    提示:
    nums.length == 3 * n
    1 <= n <= 105
    1 <= nums[i] <= 105

堆(优先队列)

枚举删除后的第n个元素(从1开始),令其下标为i,则:前n个元素是nums[0…i]中最小的n个数,后n个元素是nums[i+1…3n-1]中最大的n个数。
i ∈ \in [n-1,2n-1]
令j = i - (n-1) 则 j ∈ \in [0,n]
vLeft[j] 记录nums[0…i]中最小的n个数之和,用大根堆实现。
vRight[j]记录nums[i+1…3n-1]中最大的n个数之和,用小根堆实现。

代码

核心代码

class Solution {
public:
	long long minimumDifference(vector<int>& nums) {
		const int N = nums.size() / 3;
		vector<long long> vRet(N+1);
		{
			priority_queue<int> maxHeap;
			long long sum = 0;
			for (int i = 0; i < N; i++) {
				maxHeap.emplace(nums[i]);
				sum += nums[i];
			}
			for (int j = 0; j <= N; j++) {
				vRet[j] = sum;
				sum += nums[j + N];
				maxHeap.emplace(nums[j + N]);
				sum -= maxHeap.top();
				maxHeap.pop();
			}
		}
		{
			priority_queue<int,vector<int>,greater<>> minHeap;
			long long sum = 0;
			for (int i = 0; i < N; i++) {
				minHeap.emplace(nums[N*3-1-i]);
				sum += nums[N * 3-1 - i];
			}
			for (int j = 0; j <= N; j++) {
				vRet[N-j] -= sum;
				sum += nums[2*N-1 - j];
				minHeap.emplace(nums[2 * N-1 - j]);
				sum -= minHeap.top();
				minHeap.pop();
			}
		}
		return *std::min_element(vRet.begin(), vRet.end());
	}
};

单元测试

template<class T1, class T2>
void AssertEx(const T1& t1, const T2& t2)
{
	Assert::AreEqual(t1, t2);
}

template<class T>
void AssertEx(const vector<T>& v1, const vector<T>& v2)
{
	Assert::AreEqual(v1.size(), v2.size());
	for (int i = 0; i < v1.size(); i++)
	{
		Assert::AreEqual(v1[i], v2[i]);
	}
}

template<class T>
void AssertV2(vector<vector<T>> vv1, vector<vector<T>> vv2)
{
	sort(vv1.begin(), vv1.end());
	sort(vv2.begin(), vv2.end());
	Assert::AreEqual(vv1.size(), vv2.size());
	for (int i = 0; i < vv1.size(); i++)
	{
		AssertEx(vv1[i], vv2[i]);
	}
}

namespace UnitTest
{
	vector<int> nums;
	TEST_CLASS(UnitTest)
	{
	public:
		TEST_METHOD(TestMethod00)
		{
			nums = { 3, 1, 2 };
			auto res = Solution().minimumDifference(nums);
			AssertEx(-1LL, res);
		}
		TEST_METHOD(TestMethod01)
		{
			nums = { 7,9,5,8,1,3 };
			auto res = Solution().minimumDifference(nums);
			AssertEx(1LL, res);
		}
		TEST_METHOD(TestMethod02)
		{
			nums = { 16,46,43,41,42,14,36,49,50,28,38,25,17,5,18,11,14,21,23,39,23 };
			auto res = Solution().minimumDifference(nums);
			AssertEx(-14LL, 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++**实现。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

闻缺陷则喜何志丹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值