leetcode
leetcode 1601 ~ 1650
通过最少操作次数使数组的和相等

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

难度:

标签:

题目描述

代码结果

运行时间: 68 ms, 内存: 20.6 MB


/*
 * 思路:
 * 1. 计算两个数组的和,确定哪个数组的和更大,计算出需要调整的差值。
 * 2. 使用一个数组记录可以通过每个元素的变换(增加或者减少)来获得的变化量。
 * 3. 对变化量进行排序,从最大的变化量开始,逐个应用变化量,直到达到目标差值。
 * 4. 返回最少的操作次数,如果无法达到目标差值,返回-1。
 */

import java.util.Arrays;

public class Solution {
    public int minOperations(int[] nums1, int[] nums2) {
        int sum1 = Arrays.stream(nums1).sum();
        int sum2 = Arrays.stream(nums2).sum();
        if (sum1 == sum2) return 0;
        if (sum1 > sum2) return minOperationsHelper(nums1, nums2, sum1 - sum2);
        else return minOperationsHelper(nums2, nums1, sum2 - sum1);
    }

    private int minOperationsHelper(int[] larger, int[] smaller, int diff) {
        int[] changes = Arrays.stream(larger).map(num -> num - 1)
                            .boxed()
                            .sorted((a, b) -> b - a)
                            .mapToInt(Integer::intValue)
                            .toArray();
        changes = Arrays.copyOf(changes, changes.length + smaller.length);
        int index = larger.length;
        for (int num : smaller) changes[index++] = 6 - num;
        Arrays.sort(changes);
        int operations = 0;
        for (int i = changes.length - 1; i >= 0 && diff > 0; i--) {
            diff -= changes[i];
            operations++;
        }
        return diff > 0 ? -1 : operations;
    }
}

解释

方法:

首先计算两个数组的和,如果一个数组的和大于另一个数组,将它们交换,以便始终让a是较小的和。然后检查是否有可能通过修改使两个数组和相等,即检查数组长度的6倍是否大于另一个数组的长度,如果是则返回-1。使用Counter来计数每个数组中各数字的出现次数。然后计算两数组和之差de。通过从1到6遍历,尝试减少这个差距。对于每个数字i,计算可以通过将nums1中的i增加到6或将nums2中的7-i减少到1来改变差距的总次数。如果通过当前数字能够完全消除差距,则直接返回结果;如果不能,则减少差距并继续处理。这个过程中,ans累加操作次数,直到差距为0。

时间复杂度:

O(n + m)

空间复杂度:

O(1)

代码细节讲解

🦆
为什么在判断两个数组和是否可以通过修改变得相等时,需要检查数组长度的6倍是否大于另一个数组的长度?
这个检查是为了确保在最极端的情况下(即一个数组的所有元素都是1,另一个数组的所有元素都是6),也有可能通过修改使两个数组的和相等。如果不满足这个条件,即使把一个数组的所有元素改为1,另一个数组的所有元素改为6,两个数组的元素个数的差距也太大,导致即便是在极端情况下也无法通过增减操作使两个数组的和相等。因此,这是一个基本的可行性检查,确保算法执行前的基本条件满足。
🦆
在算法中,`ceil(de / (6 - i))`的计算方法为何能确保最小操作次数,这里的逻辑是怎样的?
在算法中,`ceil(de / (6 - i))`表示为了消除剩余的差额`de`,在当前数字`i`可以提供的单次最大改变量`6-i`下,需要进行的最小操作次数。使用`ceil`函数是因为即使差额不能被`6-i`完全整除,也需要进行一次完整的操作来确保差额被完全消除。例如,如果差额为10,而`6-i`为5,则需要进行`ceil(10 / 5) = 2`次操作。这保证了在每一个步骤中我们都在进行最优的操作,即每次尽可能多地减少差额,从而确保操作次数是最少的。
🦆
为什么选择从1到6遍历数字,并计算通过增加或减少来改变差距的次数,这样的策略为何是高效的?
选择从1到6遍历数字是因为数组中的元素值范围是这个区间内的整数。算法需要评估每个元素值的变化对总和差`de`的影响。通过考虑将nums1中的元素增加到6(最大值)或将nums2中的元素减少到1(最小值),我们可以最大化单次操作对差值的影响。这种策略高效的原因在于它直接针对最大可行的单次差值变化,从而减少了总的操作次数,加快了收敛速度,提高了算法的效率。

相关问题