给你两个下标从 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.length1 <= n <= 10^50 <= nums1[i], nums2[i] <= 10^50 <= k1, k2 <= 10^9
分析:题目要求通过最多 k1 + k2 次操作,使两个数组对应元素差值的平方和最小。由于每次操作都可以使 nums1 或 nums2 中的某个元素增加或减少 1,因此无论操作哪个数组,都相当于将对应位置的绝对差值减少 1。可以将两种操作合并考虑,令 k = k1 + k2,并构造差值数组 diff,其中 diffi = abs(nums1i - nums2i)。
接下来需要考虑如何分配这 k 次操作。
对于一个差值 x,将其减少 1,平方和就会减少 x² - (x - 1)² = 2x - 1。可以发现,差值越大,减少 1 所带来的收益越大。因此,应该优先减小较大的差值。
基于这一贪心策略,首先将 diff 按照从大到小的顺序排序,并在数组末尾添加一个 0,方便处理所有差值都被减小到 0 的情况。随后遍历排序后的数组,使用变量 val 表示当前需要处理的差值大小,cnt 表示已经处理的差值数量,初始时 val = diff0,cnt = 1。
对于当前遍历到的 diffi,希望将前 cnt 个差值统一减小到 diffi,所需要的操作次数为 temp = (val - diffi) × cnt。根据剩余操作次数 k,可以分为以下两种情况:
情况一:k >= temp。说明剩余操作次数足够将前 cnt 个差值全部减小到 diffi。此时直接消耗 temp 次操作,更新 k = k - temp,并令 val = diffi,cnt 加 1,继续处理下一个差值。
情况二:k < temp。说明剩余操作次数不足以将前 cnt 个差值全部减小到 diffi,此时需要将剩余操作尽可能均匀地分配给这 cnt 个差值。令 q = k / cnt,r = k % cnt。其中,q 表示每个差值至少可以减少的次数,r 表示剩余的操作次数。
经过操作后:
-
有 cnt - r 个差值变为 hi = val - q。
-
有 r 个差值变为 hi - 1。
-
从下标 i 开始的其余差值保持不变。
此时所有操作均已分配完毕,只需要计算上述差值的平方和,再加上尚未处理的其余差值的平方和,即可得到最终答案,并结束遍历。
如果遍历结束后仍有剩余操作次数,说明所有差值都已经减小到 0,此时最小平方和为 0。
cpp
class Solution {
public:
static bool cmp(const int a,const int b)
{
return a>b?true:false;
}
long long minSumSquareDiff(vector<int>& nums1, vector<int>& nums2, int k1, int k2) {
int n=nums1.size();
vector<int>diff;
for(int i=0;i<n;++i)
diff.push_back(abs(nums1[i]-nums2[i]));
sort(diff.begin(),diff.end(),cmp);
diff.push_back(0);
long long k=1LL*k1+1LL*k2,ans=0;
int cnt=1,val=diff[0];
for(int i=1;i<=n;++i)
{
long long temp=val-diff[i];
if(k>=temp*cnt)k-=temp*cnt,val=diff[i],cnt++;
else
{
long long q=k/cnt,r=k%cnt,hi=diff[i-1]-q;
ans+=1LL*hi*hi*(i-r);
ans+=1LL*(hi-1)*(hi-1)*r;
for(int j=i;j<n;++j)
ans+=1LL*diff[j]*diff[j];
break;
}
}
return ans;
}
};