【2022-12-07每日一题】1775. 通过最少操作次数使数组的和相等[Medium]

2022-12-07
4分钟阅读时长

2022-12-07每日一题:1775. 通过最少操作次数使数组的和相等

难度:Medium

标签:贪心 、 数组 、 哈希表 、 计数

给你两个长度可能不等的整数数组 nums1nums2 。两个数组中的所有值都在 16 之间(包含 16)。

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

请你返回使 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 <= 105
  • 1 <= nums1[i], nums2[i] <= 6

方法一:贪心 + 哈希表【官方】

func minOperations(nums1 []int, nums2 []int) int {
    m, n := len(nums1), len(nums2)
    if 6*m < n || 6*n < m {
        return -1
    }
    var cnt1, cnt2 [7]int
    diff := 0
    for _, x := range nums1 {
        cnt1[x]++
        diff += x
    }
    for _, x := range nums2 {
        cnt2[x]++
        diff -= x
    }
    if diff == 0 {
        return 0
    }
    if diff > 0 { // nums1 > nums2
        return help(cnt2, cnt1, diff)
    }
    return help(cnt1, cnt2, -diff)
}

func help(h1 [7]int, h2 [7]int, diff int) (res int) {
    h := [7]int{}
    for i := 1; i < 7; i++ {
        h[6-i] += h1[i]
        h[i-1] += h2[i]
    }
    for i := 5; i > 0 && diff > 0; i-- {
        t := min((diff+i-1)/i, h[i])
        res += t
        diff -= t * i
    }
    return res
}

func min(a, b int) int {
    if a > b {
        return b
    }
    return a
}

复杂度分析

  • 时间复杂度:O(n+m),其中 n,m 分别为数组 nums1,nums2的长度。

  • 空间复杂度:O(C),其中 C 为数组 nums1,nums2中元素值的取值空间,主要为用数组来模拟「哈希表」的空间开销。

方法二:灵茶山艾府

func minOperations(nums1, nums2 []int) (ans int) {
    if 6*len(nums1) < len(nums2) || 6*len(nums2) < len(nums1) {
        return -1 // 优化
    }
    d := 0 // 数组元素和的差,我们要让这个差变为 0
    for _, x := range nums2 { // nums2 在前
        d += x
    }
    for _, x := range nums1 { // nums1 在后
        d -= x
    }
    if d < 0 {
        d = -d
        // 统一让 nums1 的数变大,nums2 的数变小
        nums1, nums2 = nums2, nums1
    }
    cnt := [6]int{} // 统计每个数的最大变化量
    for _, x := range nums1 {
        cnt[6-x]++ // nums1 的变成 6
    }
    for _, x := range nums2 {
        cnt[x-1]++ // nums2 的变成 1
    }
    // 从大到小枚举最大变化量 5 4 3 2 1
    for i := 5; ; i++ {
        if cnt[i]*i >= d { // 可以让 d 变为 0
            return ans + (d+i-1)/i
        }
        ans += cnt[i] // 需要所有最大变化量为 i 的数
        d -= cnt[i] * i
    }
}

复杂度分析

  • 时间复杂度:O(n+m),其中 n 为 nums1的长度,m 为 nums2的长度。

  • 空间复杂度:O(C)。本题 C=6。

方法三:ylb

func minOperations(nums1, nums2 []int) (ans int) {
    s1, s2 := sum(nums1), sum(nums2)
    if s1 == s2 {
        return 0
    }
    // 处理成 nums1 小于 nums2 
    if s1 > s2 {
        nums1, nums2 = nums2, nums1
        s1, s2 = s2, s1
    }
    d := s2 - s1
    cnt := [6]int{}
    for _, x := range nums1 {
        cnt[6-x]++ // x 变大
    }
    for _, x := range nums2 {
        cnt[x-1]++ // x 变小
    }
    for i := 5; i > 0; i-- {
        for cnt[i] > 0 && d > 0 {
            d -= i
            cnt[i]--
            ans++
        }
    }
    if d <= 0 {
        return ans
    }
    return -1
}

func sum(nums []int) (s int) {
    for _, x := range nums {
        s += x
    }
    return s
}

复杂度分析

  • 时间复杂度:O(n+m),其中 n 为 nums1的长度,m 为 nums2的长度。

  • 空间复杂度:O(C)。本题 C=6。

LeetCode题库地址