LeetCode 2333.最小差值平方和:排序 + 数学计算(不用二分)

【LetMeFly】2333.最小差值平方和:排序 + 数学计算(不用二分)

力扣题目链接:https://leetcode.cn/problems/minimum-sum-of-squared-difference/

给你两个下标从 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

解题方法:排序 + 数学计算

已知两个前提:

  1. 假设有多个正整数只能选其中一个减少 1 1 1,想要使平方和最小,应该减小哪个数?

    应该减小最大的那个数。

    假设 a > b a\gt b a>b,那么 ( a − 1 ) 2 + b 2 = a 2 + b 2 + 1 − 2 a (a-1)^2 + b^2 = a^2 + b^2 + 1 - 2a (a−1)2+b2=a2+b2+1−2a, a 2 + ( b − 1 ) 2 = a 2 + b 2 + 1 − 2 b a^2 + (b-1)^2 = a^2 + b^2 + 1 - 2b a2+(b−1)2=a2+b2+1−2b,因为 a > b a\gt b a>b,所以 − 2 a < − 2 b -2a \lt -2b −2a<−2b,所以减小 a a a更优。

  2. 对nums1的操作,总有办法对nums2的等价操作。

    所以对nums1进行最多k1次操作、对nums2进行最多k2次操作,相当于最多进行k1 + k2次加减一操作。

    因为如果想要对nums1[i] + 1然后和nums2[i]求差值平方但是k1已经用完了的话,直接让nums2[i] - 1然后和nums1[i]求差值平方是等价的。

所以,我们直接求出两个数组一一对应两元素的差值数组diffs,其中diffs[i] = abs(nums1[i] - nums2[i]),每次减少diffs中最大的元素共计操作至多k1 + k2次(最多减少到 0 0 0),最终的diffs数组中每个元素的平方和即为所求。

但是,k1 + k2可能是 10 9 10^9 109级别的,所以我们不能真的一个一个地去试,而是一次减多个元素,每个元素减好几。

具体而言,对diffs数组从大到小排序。假设当前元素是diffs[i](下一个元素是diffs[i + 1]),那么我们可以尽量把从diffs[0]到diffs[i]的所有元素都减小到diffs[i + 1](共计需要减少 ( d i f f s i − d i f f s i + 1 ) × ( i + 1 ) (diffsi - diffsi + 1) \times (i + 1) (diffsi−diffsi+1)×(i+1)次):

  1. 如果剩余的k足够多( k ≥ ( d i f f s i − d i f f s i + 1 ) ∗ ( i + 1 ) k\geq (diffsi - diffsi + 1) * (i + 1) k≥(diffsi−diffsi+1)∗(i+1)),那么就把diffs[0]到diffs[i]都减小到diffs[i + 1](消耗 ( d i f f s i − d i f f s i + 1 ) × ( i + 1 ) (diffsi - diffsi + 1) \times (i + 1) (diffsi−diffsi+1)×(i+1)次操作),然后继续处理下一个元素。

  2. 如果剩余的k不够多( k < ( d i f f s i − d i f f s i + 1 ) ∗ ( i + 1 ) k < (diffsi - diffsi + 1) * (i + 1) k<(diffsi−diffsi+1)∗(i+1)),那么只能把 k m o d    ( i + 1 ) k \mod (i + 1) kmod(i+1)个元素减少 ⌊ k i + 1 ⌋ + 1 \lfloor\frac{k}{i+1}\rfloor+1 ⌊i+1k⌋+1以及把 i + 1 − ( k m o d    ( i + 1 ) ) i+1-(k \mod (i + 1)) i+1−(kmod(i+1))个元素减少 ⌊ k i + 1 ⌋ \lfloor\frac{k}{i+1}\rfloor ⌊i+1k⌋。

    到此为止,k已经消耗完毕,就不需要再继续遍历了,开始计算。 0 0 0到 i i i的diff中有 k m o d    ( i + 1 ) k \mod (i + 1) kmod(i+1)个diff是 d i f f s i − ⌊ k i + 1 ⌋ − 1 diffsi - \lfloor\frac{k}{i+1}\rfloor - 1 diffsi−⌊i+1k⌋−1,有 i + 1 − ( k m o d    ( i + 1 ) ) i + 1 - (k \mod (i + 1)) i+1−(kmod(i+1))个diff是 d i f f s i − ⌊ k i + 1 ⌋ diffsi - \lfloor\frac{k}{i+1}\rfloor diffsi−⌊i+1k⌋,剩余的diffs[i + 1]到diffs[n - 1]是原值不变。

特别的,我们可以向diffs数组后面插入一个哨兵元素 0 0 0。如果循环结束了,说明每个diff都减少到了 0 0 0,直接返回 0 0 0。

时空复杂度

  • 时间复杂度 O ( N 2 ) O(N^2) O(N2)
  • 空间复杂度 O ( N log ⁡ N ) O(N\log N) O(NlogN)

AC代码

C++
cpp 复制代码
/*
 * @LastEditTime: 2026-10-12 19:25:34
 */
typedef long long ll;
class Solution {
public:
    ll minSumSquareDiff(vector<int>& nums1, vector<int>& nums2, int k1, int k2) {
        int n = nums1.size();
        vector<int> diffs(nums1.size() + 1);  // 末尾多一个哨兵
        for (int i = 0; i < n; i++) {
            diffs[i] = abs(nums1[i] - nums2[i]);
        }
        ranges::sort(diffs, greater<int>());

        int k = k1 + k2;
        for (int i = 0; i < n; i++) {
            ll per_diff = diffs[i] - diffs[i + 1];
            ll all_need = per_diff * (i + 1);
            if (k >= all_need) {
                k -= all_need;
                continue;
            }
            /*
                不够全部减小到目标值了,看看能减小到多少。
                假设k = 3, i = 1, all_need = 5
                k // 2 = 1,2个数每个减少1
                k % 2 = 1,其中1个数再减少一
            */
            ll all_sub = k / (i + 1);
            ll more_sub_1 = k % (i + 1);
            ll not_more_sub_1 = i + 1 - more_sub_1;
            diffs[i] -= all_sub;
            ll ans = not_more_sub_1 * diffs[i] * diffs[i];
            ans += more_sub_1 * (diffs[i] - 1) * (diffs[i] - 1);
            for (i++; i < n; i++) {
                ans += (ll) diffs[i] * diffs[i];
            }
            return ans;
        }
        return 0;
    }
};

End

同步发文于CSDN和我的个人博客,原创不易,转载经作者同意后请附上原文链接哦~

千篇源码题解已开源

相关推荐
aqiu11111144 分钟前
【C++算法打怪专栏】LeetCode 217. 存在重复元素(解法总结)
c++·算法·leetcode
yi0112 小时前
DAY28: LeetCode 111. 二叉树的最小深度
人工智能·笔记·python·算法·leetcode·二叉树·递归
wuyk5552 小时前
27.LeetCode 2265. 统计值等于子树平均值的节点数 —— 后序遍历实战
stm32·单片机·嵌入式硬件·算法
xiangyun612 小时前
C++ list 模拟实现(二):const迭代器与反向迭代器,模板的黑魔法
开发语言·数据库·c++·算法·stl·list
xxwl5853 小时前
三、区域赛算法模版第三章---字符串相关算法
笔记·学习·算法
信奥卷王3 小时前
[GESP202509 六级] 划分字符串(含视频)
数据结构·算法
酒醉的胡铁3 小时前
相似度搜索的常见算法
算法·ai
局部有解3 小时前
Leetcode Hot100 - 最长连续序列
算法·leetcode·职场和发展
行者全栈架构师4 小时前
给开源 Rust 工具加一个新检查项,TRAE 帮我从想法跑到构建产物:全链路实战
前端·算法·架构