通过最少操作次数使数组的和相等

107 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第18天,点击查看活动详情

1775. 通过最少操作次数使数组的和相等 - 力扣(LeetCode)

给你两个长度可能不等的整数数组 nums1 和 nums2 。两个数组中的所有值都在 1 到 6 之间(包含 1 和 6)。

每次操作中,你可以选择 任意 数组中的任意一个整数,将它变成 1 到 6 之间 任意 的值(包含 1 和 6)。

请你返回使 nums1 中所有数的和与 nums2 中所有数的和相等的最少操作次数。如果无法使两个数组的和相等,请返回 -1 。

示例 1:

输入: nums1 = [1,2,3,4,5,6], nums2 = [1,1,2,2,2,2]
输出: 3
解释: 你可以通过 3 次操作使 nums1 中所有数的和与 nums2 中所有数的和相等。以下数组下标都从 0 开始。
- 将 nums2[0] 变为 6 。 nums1 = [1,2,3,4,5,6], nums2 = [6,1,2,2,2,2] 。
- 将 nums1[5] 变为 1 。 nums1 = [1,2,3,4,5,1], nums2 = [6,1,2,2,2,2] 。
- 将 nums1[2] 变为 2 。 nums1 = [1,2,2,4,5,1], nums2 = [6,1,2,2,2,2] 。

示例 2:

输入: nums1 = [1,1,1,1,1,1,1], nums2 = [6]
输出: -1
解释: 没有办法减少 nums1 的和或者增加 nums2 的和使二者相等。

示例 3:

输入: nums1 = [6,6], nums2 = [1]
输出: 3
解释: 你可以通过 3 次操作使 nums1 中所有数的和与 nums2 中所有数的和相等。以下数组下标都从 0 开始。
- 将 nums1[0] 变为 2 。 nums1 = [2,6], nums2 = [1] 。
- 将 nums1[1] 变为 2 。 nums1 = [2,2], nums2 = [1] 。
- 将 nums2[0] 变为 4 。 nums1 = [2,2], nums2 = [4] 。

提示:

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

思路

一个长度为n的数组和最大值为6*n,最小值为n。当nums1nums2的长度比大于6时,无法使二者相等,即较短的数组最大值小于较长数组的最小值,无论怎么操作都无法相等。

nums1的和大于nums2的和,和之差为diff。我们需要修改nums1和nums2的值使diff0。为了使操作步骤最少,nums1中的值应该往小修改,nums2的值应该往大修改,设每次操作后的新值和原始值的差为c,我们应该让c最大化。

首先先把nums1降序排列,nums2升序排列,遍历nums1nums2,记当前下标分别为ijc1 = nums1[i] - 1, c2 = 6 - nums2[j],当c1 > c2 时,diff = diff - c1i++,否则 diff = diff - c2j++,当diff <= 0时跳出循环。

因为数组值范围在16之间,我们可以用两个数组cs1cs2分别记录nums1nums216每个值出现的次数,我们用i遍历16
diff1 = diff - (6 - i + 1 - 1) * cs1[6 - i + 1] + (6 - i) * cs2[i] = diff - (6 - i) * (cs1[6 - i + 1] + cs2[i])

  • diff > 0时,操作步骤增加cs1[6 - i + 1] + cs2[i]diff = diff1
  • diff <= 0时,操作步骤增加Math.ceil(diff / (6 - i)),跳出循环。

解题

/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @return {number}
 */
var minOperations = function (nums1, nums2) {
  let m = nums1.length;
  let n = nums2.length;
  if (m > n * 6 || n > m * 6) return -1;
  const counter = new Array(7).fill(0);
  let diff = 0;
  for (let i = 0; i < m; i++) {
    diff += nums1[i];
    counter[nums1[i]]++;
  }
  for (let i = 0; i < n; i++) {
    diff -= nums2[i];
    counter[6 - nums2[i] + 1]++;
  }
  let offset = diff > 0 ? 1 : 6;
  let flag = diff > 0 ? 1 : -1;
  let res = 0;
  diff = Math.abs(diff);
  for (let i = 5; i >= 1; i--) {
    const count = counter[flag * i + offset];
    const num = Math.ceil(diff / i);
    if (count >= num) {
      res += num;
      break;
    } else {
      res += count;
      diff -= i * count;
    }
  }
  return res;
};