【每日一题Day49】LC1775通过最少操作数使数组和相等 | 贪心 + 哈希表 + 双指针

57 篇文章 0 订阅
44 篇文章 0 订阅

通过最少操作数使数组和相等【LC1775】

You are given two arrays of integers nums1 and nums2, possibly of different lengths. The values in the arrays are between 1 and 6, inclusive.

In one operation, you can change any integer’s value in any of the arrays to any value between 1 and 6, inclusive.

Return the minimum number of operations required to make the sum of values in nums1 equal to the sum of values in nums2. Return -1 if it is not possible to make the sum of the two arrays equal.

想正常收快递想出校或者就让我回家吧
就是说为什么我写的代码那么长…

  • 思路:

    • 首先统计数组nums1nums2的和sum1sum2,如果sum1==sum2,那么直接返回0,否则需要判断是否能使数组nums1nums2的和相等。如果可以,那么需要的最少操作数是多少?

    • 如何判断是否能使数组nums1nums2的和相等?

      假设 s u m 1 > s u m 2 sum1>sum2 sum1>sum2 d i v = s u m 1 − s u m 2 div=sum1-sum2 div=sum1sum2,我们应该尽最大可能减小sum1、并尽最大可能增大sum2nums2如果数组nums1能够减小的最大数值+数组nums2能够增大的最大数值小于div,那么不可能使两个数组的和相等;反之,可以使两个数组的和相等

    • 如何获得最少操作数?

      • 贪心的思想:

        局部最优:一次操作尽最大可能减小 d i v div div

        全局最优:使用最少的操作数使 d i v div div达到0

      • 那么对于数组nums1我们应该从最大值max1开始减小,对于数组nums2我们应该从最小值min2开始增大,并且每次减小或者增大的值应该是允许范围内的最大值。【使用双指针定位】

        • 如果 m a x 1 − M I N _ N U M > d i v max1-MIN\_NUM>div max1MIN_NUM>div,那么直接将max1减小至最小值MIN_NUM,此时div减小的值为 m a x 1 − M I N _ N U M max1-MIN\_NUM max1MIN_NUM
        • 如果 M A X _ N U M − m i n 2 > d i v MAX\_NUM-min2>div MAX_NUMmin2>div,那么直接将min2扩大至最大值MAX_NUM,此时div`减小的值为 M A X _ N U M − m i n 2 MAX\_NUM-min2 MAX_NUMmin2
        • 如果 m a x 1 − M I N _ N U M < = d i v max1-MIN\_NUM<=div max1MIN_NUM<=div,那么只需要一次操作就可以将div变为0,此时将max1减小至最小值max1-div,此时div减小的值为 d i v div div
        • 如果 M A X _ N U M − m i n 2 < = d i v MAX\_NUM-min2<=div MAX_NUMmin2<=div,那么只需要一次操作就可以将div变为0,此时将min2扩大至最大值min2+div,此时div减小的值为 d i v div div
  • 实现:

    为了快速获得数组nums1nums2的最大值和最小值,可以使用哈希表统计每个数字出现的次数map1map2,倒序遍历map1获得max1,正序遍历map2获得min2【也可以将数组排序】

    class Solution {
        int MAX_NUM = 6;
        int MIN_NUM = 1;
        public int minOperations(int[] nums1, int[] nums2) {    
            int sum1 = 0;
            int sum2 = 0;
            int[] map1 = new int[MAX_NUM + 1];
            int[] map2 = new int[MAX_NUM + 1];
            // Arrays.sort(nums1);
            // Arrays.sort(nums2);
            for (int num1 :nums1){
                sum1 += num1;
                map1[num1]++;
            }
            for (int num2 : nums2){
                sum2 += num2;
                map2[num2]++;
            }
            // 不可能相等的情况
            // nums1能够减小的数值 + nums2能够增大的数值 < dv
            if (sum1 > sum2){
                if (isPossible(sum1, sum2, nums1.length, nums2.length, sum1 - sum2)){
                    return getMinOperations(map1, map2, sum1 - sum2);
                }else{
                    return -1;
                }           
            }else if (sum1 < sum2 ){
                if (isPossible(sum2, sum1, nums2.length, nums1.length, sum2 - sum1)){
                    return getMinOperations(map2, map1, sum2 - sum1);
                }else{
                    return -1;
                }
            }
            return 0;
        }
        public boolean isPossible (int sum1, int sum2, int len1, int len2, int dv){
            // 不可能相等的情况
            // nums1能够减小的数值 + nums2能够增大的数值 < dv
            int a = sum1 - len1 * MIN_NUM;// 全部减小至1
            int b = len2 * MAX_NUM - sum2;//全部增大至6
            return a + b >= dv;
        }
        // nums1和大于nums2
        // 均为排序数组
        public int getMinOperations(int[] map1, int[] map2, int dv){                
            int r = MAX_NUM;
            int l = MIN_NUM;
            int ans = 0;
            while (l < MAX_NUM || r > MIN_NUM){
                // 如果dv >= r - MIN_NUM, 缩小至MIN_VALUE
                while (dv != 0 && dv > r - MIN_NUM && map1[r] > 0){
                    dv -= r - MIN_NUM;
                    map1[r]--;
                    map1[MIN_NUM]++;
                    ans++;
                }
                // 如果dv <= MAX_NUM - 1, 扩大至MAX_VALUE
                while (dv != 0 && dv > MAX_NUM - l && map2[l] > 0 ){
                    dv -= MAX_NUM - l;
                    map2[l]--;
                    map2[MAX_NUM]++;
                    ans++;
                }
                // 如果dv < r - MIN_NUM, 只需要一次操作就可以使dv变为0
                while (dv != 0 && dv <= r - MIN_NUM && map1[r] > 0){
                    dv -= dv;
                    map1[r]--;
                    map1[r - dv]++;
                    ans++;
                }
                // 如果dv < MAX_NUM - 1,, 只需要一次操作就可以使dv变为0
                while (dv != 0 && dv <= MAX_NUM - l && map2[l] > 0 ){
                    dv -= dv;
                    map2[l]--;
                    map2[l + dv]++;
                    ans++;
                }
                if (dv == 0){
                    break;
                }
                r--;
                l++;
            }
            return ans;
    
    
        }
    }
    
    • 复杂度
      • 时间复杂度: O ( n + m ) O(n+m) O(n+m),n和m分别为数组nums1和nums2的长度
      • 空间复杂度: O ( C ) O(C) O(C), C C C为哈希表的空间
  • 思路类似,不统计每个数字对应的个数,直接统计每个元素的最大变化量

class Solution {
    public int minOperations(int[] nums1, int[] nums2) {
        if (6 * nums1.length < nums2.length || 6 * nums2.length < nums1.length)
            return -1; // 优化
        // int d = Arrays.stream(nums2).sum() - Arrays.stream(nums1).sum();
        int d = 0; // 数组元素和的差,我们要让这个差变为 0
        for (int x : nums2) d += x;
        for (int x : nums1) d -= x;
        if (d < 0) {
            d = -d;
            int[] tmp = nums1;
            nums1 = nums2;
            nums2 = tmp; // 交换,统一让 nums1 的数变大,nums2 的数变小
        }
        int[] cnt = new int[6]; // 统计每个数的最大变化量
        for (int x : nums1) ++cnt[6 - x]; // nums1 的变成 6
        for (int x : nums2) ++cnt[x - 1]; // nums2 的变成 1
        for (int i = 5, ans = 0; ; --i) { // 从大到小枚举最大变化量 5 4 3 2 1
            if (i * cnt[i] >= d) // 可以让 d 变为 0
                return ans + (d + i - 1) / i;
            ans += cnt[i]; // 需要所有最大变化量为 i 的数
            d -= i * cnt[i];
        }
    }
}

作者:灵茶山艾府
链接:https://leetcode.cn/problems/equal-sum-arrays-with-minimum-number-of-operations/solutions/2009786/mei-xiang-ming-bai-yi-ge-dong-hua-miao-d-ocuu/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值