[LeetCode] 1818. Minimum Absolute Sum Difference

You are given two positive integer arrays nums1 and nums2, both of length n.

The absolute sum difference of arrays nums1 and nums2 is defined as the sum of |nums1[i] - nums2[i]| for each 0 <= i < n (0-indexed).

You can replace at most one element of nums1 with any other element in nums1 to minimize the absolute sum difference.

Return the minimum absolute sum difference after replacing at most one element in the array nums1. Since the answer may be large, return it modulo 109 + 7.

|x| is defined as:

  • x if x >= 0, or
  • -x if x < 0.

Example 1:

Input: nums1 = [1,7,5], nums2 = [2,3,5]
Output: 3
Explanation: There are two possible optimal solutions:
- Replace the second element with the first: [1,7,5] => [1,1,5], or
- Replace the second element with the third: [1,7,5] => [1,5,5].
Both will yield an absolute sum difference of |1-2| + (|1-3| or |5-3|) + |5-5| = 3.

Example 2:

Input: nums1 = [2,4,6,8,10], nums2 = [2,4,6,8,10]
Output: 0
Explanation: nums1 is equal to nums2 so no replacement is needed. This will result in an 
absolute sum difference of 0.

Example 3:

Input: nums1 = [1,10,4,4,2,7], nums2 = [9,3,5,1,7,4]
Output: 20
Explanation: Replace the first element with the second: [1,10,4,4,2,7] => [10,10,4,4,2,7].
This yields an absolute sum difference of |10-9| + |10-3| + |4-5| + |4-1| + |2-7| + |7-4| = 20

Constraints:

  • n == nums1.length
  • n == nums2.length
  • 1 <= n <= 105
  • 1 <= nums1[i], nums2[i] <= 105

绝对差值和。

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

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

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

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

|x| 定义为:

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

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

这道题的题设是请你找两个数组每一对数字之间的差值 nums1[i] - nums2[i] 的绝对值并累加(我之后暂且用 a 和 b表示),并且通过把 a 替换成 nums1 的某个别的元素 c,使得最终的累加和最小。所以有如下等式,

  • 原来每一对数字对结果集的贡献是 Math.abs(a - b)
  • 如果我们用另一个元素去替换了 a,我们得到的贡献是 Math.abs(c - b)
  • 改变前后的差值为 Math.abs(a - b) - Math.abs(c - b)
  • 因为需要最小化全局的绝对差值和,所以我们需要找到一个 c,使 Math.abs(c - b) 尽量小,那么就能使改变前后的差值 Math.abs(a - b) - Math.abs(c - b) 尽可能大

这道题我提供两种做法,一种是二分法,一种会利用到treeset。

首先是二分法。因为不能对 input 数组排序,但是二分一般是在一个有序的数组里。所以这里我们对 nums1 数组进行 clone,并且对 clone 的数组排序,记为 sorted。此时我们开始一一比较 nums1[i] - nums2[i],并在过程中记录累加和。对于每一对 nums1[i] - nums2[i],他们一开始默认的差值的绝对值,我们记为 diff。接着我们用二分法,在 clone 数组里找一个 target,看看是否有可能让 nums1[i] - nums2[i] 尽可能地小,从而使得最后的绝对差值和更小。注意这道题的二分需要比较两次,因为对于当前的 nums2[i] 来说,我们需要比较一下他与哪个数字更接近(sorted[i] 和 sorted[i + 1])。

时间O(nlogn)

空间O(n) - sorted array

Java实现

 1 class Solution {
 2     public int minAbsoluteSumDiff(int[] nums1, int[] nums2) {
 3         int MOD = (int) Math.pow(10, 9) + 7;
 4         int len = nums1.length;
 5         int[] sorted = new int[len];
 6         sorted = nums1.clone();
 7         Arrays.sort(sorted);
 8         long sum = 0;
 9         long max = 0;
10         for (int i = 0; i < len; i++) {
11             int a = nums1[i];
12             int b = nums2[i];
13             int diff = Math.abs(a - b);
14             sum = (sum + diff) % MOD;
15             int j = helper(sorted, b);
16             if (j < len) {
17                 max = Math.max(max, diff - (sorted[j] - b));
18             }
19             if (j > 0) {
20                 max = Math.max(max, diff - (b - sorted[j - 1]));
21             }
22         }
23         return (int) (sum - max + MOD) % MOD;
24     }
25 
26     private int helper(int[] nums, int target) {
27         int left = 0;
28         int right = nums.length - 1;
29         if (nums[right] < target) {
30             return right + 1;
31         }
32         while (left < right) {
33             int mid = left + (right - left) / 2;
34             if (nums[mid] < target) {
35                 left = mid + 1;
36             } else {
37                 right = mid;
38             }
39         }
40         return left;
41     }
42 }

treeset 的做法背后的原理其实跟二分法很类似,只不过我们用二分,像是自己手动在找那个 c 元素,而 treeset 则代替了这项工作。

时间O(nlogn)

空间O(n) - treeset

Java实现

 1 class Solution {
 2     public int minAbsoluteSumDiff(int[] nums1, int[] nums2) {
 3         int MOD = (int) Math.pow(10, 9) + 7;
 4         int len = nums1.length;
 5         int sum = 0;
 6         for (int i = 0; i < len; i++) {
 7             sum = (sum + Math.abs(nums1[i] - nums2[i])) % MOD;
 8         }
 9         // corner case
10         if (sum == 0) {
11             return 0;
12         }
13         // normal case
14         TreeSet<Integer> treeset = new TreeSet<>();
15         for (int num : nums1) {
16             treeset.add(num);
17         }
18         int maxProfit = 0;
19         for (int i = 0; i < len; i++) {
20             int a = nums1[i];
21             int b = nums2[i];
22             int diff = Math.abs(a - b); // 不替换前的 |nums1[i] - nums2[i]|
23             int c = helper(treeset, b);
24             int newDiff = Math.abs(c - b);
25             // 替换当前i位置,没有收益,继续下一个位置尝试
26             if (newDiff >= diff) {
27                 continue;
28             }
29             maxProfit = Math.max(maxProfit, diff - newDiff);
30         }
31         return (sum + MOD - maxProfit) % MOD;
32     }
33 
34     private int helper(TreeSet<Integer> set, int num) {
35         if (set.contains(num)) {
36             return num;
37         }
38         Integer ceiling = set.ceiling(num);
39         int diff1 = ceiling != null ? Math.abs(ceiling - num) : Integer.MAX_VALUE;
40         Integer floor = set.floor(num);
41         int diff2 = floor != null ? Math.abs(floor - num) : Integer.MAX_VALUE;
42         return diff1 < diff2 ? ceiling : floor;
43     }
44 }

LeetCode 题目总结

原文地址:https://www.cnblogs.com/cnoodle/p/15013606.html