题目

给你两个正整数数组 nums1 和 nums2 ,数组的长度都是 n 。

数组 nums1 和 nums2 的 绝对差值和 定义为所有 |nums1[i] - nums2[i]|(0 <= i < n)的 总和(下标从 0 开始)。

你可以选用 nums1 中的 任意一个 元素来替换 nums1 中的 至多 一个元素,以 最小化 绝对差值和。

在替换数组 nums1 中最多一个元素 之后 ,返回最小绝对差值和。因为答案可能很大,所以需要对 10^9 + 7 取余 后返回。

|x| 定义为:

如果 x >= 0 ,值为 x ,或者
如果 x <= 0 ,值为 -x

示例 1:

输入:nums1 = [1,7,5], nums2 = [2,3,5]
输出:3
解释:有两种可能的最优方案:

  • 将第二个元素替换为第一个元素:[1,7,5] => [1,1,5] ,或者
  • 将第二个元素替换为第三个元素:[1,7,5] => [1,5,5]
    两种方案的绝对差值和都是 |1-2| + (|1-3| 或者 |5-3|) + |5-5| = 3

示例 2:

输入:nums1 = [2,4,6,8,10], nums2 = [2,4,6,8,10]
输出:0
解释:nums1 和 nums2 相等,所以不用替换元素。绝对差值和为 0

示例 3:

输入:nums1 = [1,10,4,4,2,7], nums2 = [9,3,5,1,7,4]
输出:20
解释:将第一个元素替换为第二个元素:[1,10,4,4,2,7] => [10,10,4,4,2,7]
绝对差值和为 |10-9| + |10-3| + |4-5| + |4-1| + |2-7| + |7-4| = 20

提示:

n == nums1.length
n == nums2.length
1 <= n <= 10^5
1 <= nums1[i], nums2[i] <= 10^5

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-absolute-sum-difference
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

只允许替换1818. 绝对差值和 - 图1中的一个数字,使得差的绝对值之和最小,那么就要使替换后差绝对值减小最小。

因此,有如下思路:对于1818. 绝对差值和 - 图2中的每一个数,在1818. 绝对差值和 - 图3中找到和它最接近的数(可以使用二分或者有序集合查找),将找到的这个数替换1818. 绝对差值和 - 图4中同下标的数,这样在这个位置一定是减小最多的,然后找到其中减小最多的,返回所有的差绝对值之和减去这个最大值。

代码

  1. class Solution {
  2. public int minAbsoluteSumDiff(int[] nums1, int[] nums2) {
  3. int mod = (int) (1e9 + 7);
  4. int n = nums1.length;
  5. // 要对nums1排序,这里复制一份nums
  6. int[] nums = Arrays.copyOf(nums1, n);
  7. Arrays.sort(nums);
  8. // 最大能减小的绝对差值
  9. int maxDec = 0;
  10. int ans = 0;
  11. for (int i = 0; i < n; i++) {
  12. // diff为替换nums[i]之前的绝对差值
  13. int diff = Math.abs(nums1[i] - nums2[i]);
  14. ans = (ans + diff) % mod;
  15. // smallerDiff为替换nums1[i]之后更小的绝对差值
  16. int smallerDiff = bsearch(nums, nums2[i]);
  17. // diff - smallerDiff 为减小的绝对差值
  18. if (diff - smallerDiff > maxDec) {
  19. maxDec = diff - smallerDiff;
  20. }
  21. }
  22. // 这里要注意,由于ans在循环中不断对mod取余,这里ans可能小于maxDec,因此要加一个mod再取余
  23. return (ans - maxDec + mod) % mod;
  24. }
  25. // 找到nums中和target最接近的数x,并返回x和target的绝对差值
  26. public int bsearch(int[] nums, int target) {
  27. int n = nums.length;
  28. int low = 0;
  29. int high = n;
  30. while (low < high) {
  31. int mid = low + (high - low) / 2;
  32. if (nums[mid] < target) {
  33. low = mid + 1;
  34. } else {
  35. high = mid;
  36. }
  37. }
  38. if (low == n) {
  39. return target - nums[n - 1];
  40. }
  41. if (low == 0) {
  42. return nums[0] - target;
  43. }
  44. return Math.min(Math.abs(nums[low - 1] - target), Math.abs(nums[low] - target));
  45. }
  46. }