当前位置: 首页 > article >正文

【C++二分查找 前缀和】2333. 最小差值平方和|2011

本文涉及的基础知识点

C++算法:前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频
C++二分查找

LeetCode2333. 最小差值平方和

给你两个下标从 0 开始的整数数组 nums1 和 nums2 ,长度为 n 。
数组 nums1 和 nums2 的 差值平方和 定义为所有满足 0 <= i < n 的 (nums1[i] - nums2[i])2 之和。
同时给你两个正整数 k1 和 k2 。你可以将 nums1 中的任意元素 +1 或者 -1 至多 k1 次。类似的,你可以将 nums2 中的任意元素 +1 或者 -1 至多 k2 次。
请你返回修改数组 nums1 至多 k1 次且修改数组 nums2 至多 k2 次后的最小 差值平方和 。
注意:你可以将数组中的元素变成 负 整数。
示例 1:
输入:nums1 = [1,2,3,4], nums2 = [2,10,20,19], k1 = 0, k2 = 0
输出:579
解释:nums1 和 nums2 中的元素不能修改,因为 k1 = 0 和 k2 = 0 。
差值平方和为:(1 - 2)2 + (2 - 10)2 + (3 - 20)2 + (4 - 19)2 = 579 。
示例 2:
输入:nums1 = [1,4,10,12], nums2 = [5,8,6,9], k1 = 1, k2 = 1
输出:43
解释:一种得到最小差值平方和的方式为:

  • 将 nums1[0] 增加一次。
  • 将 nums2[2] 增加一次。
    最小差值平方和为:
    (2 - 5)2 + (4 - 8)2 + (10 - 7)2 + (12 - 9)2 = 43 。
    注意,也有其他方式可以得到最小差值平方和,但没有得到比 43 更小答案的方案。
    提示:
    n == nums1.length == nums2.length
    1 <= n <= 105
    0 <= nums1[i], nums2[i] <= 105
    0 <= k1, k2 <= 109

二分查找+前缀和

nums3 = |nums1[i]-nums2[i]|,其和为sum。
显然nums1[i],加一减一,和nums2[i]减一加一完全相同。本题    ⟺    \iff 将nums3[i]最多减1k1+k2次。
有序映射mCnt记录nums3[i]各值的数量。
有序映射mPreCnt[i]记录nums3中值小于等于i的数量。
有序映射mPreSum[i]记录nums3中值小于等于i的和。
mCnt和mPreCnt mPreSum的key都只包括nums3中的元素和0。
Check(x)
参数返回:[0,nums3的最大值]
it = mCnt.upper(x)
–it
假定修改成功后,其和为:sum1 = mPreSum[it.first] + (N - it.second)*x;
return sum1 - sum <= k1+k2。
二分求出最小x。
如果有剩余次数,如果x 不是0,将部分x变成x-1。
然后求平方和。

代码

核心代码

template<class INDEX_TYPE>
class CBinarySearch
{
public:
	CBinarySearch(INDEX_TYPE iMinIndex, INDEX_TYPE iMaxIndex):m_iMin(iMinIndex),m_iMax(iMaxIndex) {}
	template<class _Pr>
	INDEX_TYPE FindFrist( _Pr pr)
	{
		auto left = m_iMin - 1;
		auto rightInclue = m_iMax;
		while (rightInclue - left > 1)
		{
			const auto mid = left + (rightInclue - left) / 2;
			if (pr(mid))
			{
				rightInclue = mid;
			}
			else
			{
				left = mid;
			}
		}
		return rightInclue;
	}
	template<class _Pr>
	INDEX_TYPE FindEnd( _Pr pr)
	{
		int leftInclude = m_iMin;
		int right = m_iMax + 1;
		while (right - leftInclude > 1)
		{
			const auto mid = leftInclude + (right - leftInclude) / 2;
			if (pr(mid))
			{
				leftInclude = mid;
			}
			else
			{
				right = mid;
			}
		}
		return leftInclude;
	}
protected:
	const INDEX_TYPE m_iMin, m_iMax;
};

class Solution {
		public:
			long long minSumSquareDiff(vector<int>& nums1, vector<int>& nums2, int k1, int k2) {
				map<int, int> mCnt, mPreCnt;
				map<int,long long> mPreSum;
				const int N = nums1.size();
				mCnt[0] = 0;				
				for (int i = 0; i < N; i++) {
					mCnt[abs(nums1[i] - nums2[i])]++;
				}
				mPreCnt[0] = 0;
				mPreSum[0] = 0;				
				for (auto [v, cnt] : mCnt) {
					mPreCnt[v] = mPreCnt.rbegin()->second + cnt;
					mPreSum[v] = mPreSum.rbegin()->second + (long long)v * cnt;
				}
				const long long sum = mPreSum.rbegin()->second;
				auto Need = [&](int x) {
					auto it = mPreCnt.upper_bound(x);
					--it;
					const auto sum1 = mPreSum[it->first] + (long long)x * (N - it->second);
					return sum -sum1;
				};
				auto Check = [&](int x) {return Need(x) <= k1 + k2; };
				 CBinarySearch<int> bs(0, mCnt.rbegin()->first);
				 int x1 = bs.FindFrist(Check);
				 if (0 == x1) { return 0; }
				 long long ans=0;
				 int cnt1 = 0;
				 for (auto [v, cnt] : mCnt) {
					 if (v >= x1)break;
					 cnt1 += cnt;
					 ans += (long long)v * v * cnt;
				 }
				 const int iRemain = k1 + k2 - Need(x1);
				 const int xcnt = N - cnt1 - iRemain;
				 ans += (long long)(x1 - 1) * (x1 - 1) * iRemain;
				 ans += (long long)x1 * x1 * xcnt;
				 return ans;
			}
		};

单元测试

vector<int> nums1, nums2;
		int k1,k2;
		TEST_METHOD(TestMethod11)
		{
			nums1 = { 1,2,3,4 }, nums2 = { 2,10,20,19 }, k1 = 0, k2 = 0;
			auto res = Solution().minSumSquareDiff(nums1, nums2, k1, k2);
			AssertEx(579LL, res);
		}
		TEST_METHOD(TestMethod12)
		{
			nums1 = { 1,4,10,12 }, nums2 = { 5,8,6,9 }, k1 = 1, k2 = 1;
			auto res = Solution().minSumSquareDiff(nums1, nums2, k1, k2);
			AssertEx(43LL, 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++**实现。


http://www.kler.cn/a/418136.html

相关文章:

  • 单链表---移除链表元素
  • Matlab mex- setup报错—错误使用 mex,未检测到支持的编译器...
  • 共享售卖机语音芯片方案选型:WTN6020引领智能化交互新风尚
  • 【接口封装】——11、Qt 的单例模式
  • 分布式项目使用Redis实现数据库对象自增主键ID
  • python -从文件夹批量提取pdf文章的第n页,并存储起来
  • Kubernetes集群操作
  • C++编程:模拟实现CyberRT的DataVisitor和DataDispatcher
  • openwrt利用nftables在校园网环境下开启nat6 (ipv6 nat)
  • AntFlow 0.20.0版发布,增加多数据源多租户支持,进一步助力企业信息化,SAAS化
  • Python基于 Opencv+wxPython 的人脸识别上课考勤系统,附源码
  • MySQL —— MySQL 程序
  • OpenCV4.8 开发实战系列专栏之 17 - 图像直方图
  • (SAST 检测规-5)不良授权和身份验证
  • 《C++ Primer Plus》学习笔记|第9章 内存模型和名称空间 (24-12-1更新)
  • 深入理解 Docker 在 CI/CD 流程中的应用原理
  • 处理HTTP请求的两种常见方式:多个处理器(Handler)、多个处理函数(HandleFunc),两者有什么区别
  • 传智杯 A字符串拼接
  • vxe-table 树形表格的详细用法、树形表格懒加载
  • 从实战出发,精通Cache设计与性能优化
  • 【iOS】OC高级编程 iOS多线程与内存管理阅读笔记——自动引用计数(二)
  • 机器学习模型从理论到实战|【007-SVM 支持向量机】 SVM的情感分类
  • js常见函数实现
  • Ubuntu 操作系统
  • 基于51单片机的心率体温监测报警系统(包括程序、仿真、原理图、流程图)
  • 2024年9月 GESP C++等级考试 真题及解析 三级