[LeetCode 1775]通过最少操作数使数组的和相等

题目描述

题目链接:[LeetCode 1775]通过最少操作数使数组的和相等

给你两个长度可能不等的整数数组 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让其变成6可以让整个数组变大5,而挑选3最多变成6,只能让数组变大6-3=3

如图:要让数组2尽可能变大,所以第一次选择1变成6,差距缩减到25-15=10
第二次选择第二个1变成6,差距缩减到25-20=5
在这里插入图片描述

此时,第二个数组没有1了,最小是2,所以2的变化最多为4(因为6-2=4),还需要至少两步才能变到与数组1和相等,此时应该选择第一个数组中的6变成1,只需要一步就行
在这里插入图片描述

所以很明确了,我们要利用指针,指向小数组里面的最小值,和大数组里面的最大值,因为这样的数具有最大的变化能力!

代码(写得比较长,仅供参考)

class Solution {
public:
    int check(int q1[], int q2[], int s1, int s2) {
    	//默认s1 < s2,方便计算
        if(s1 > s2) return check(q2, q1, s2, s1);
        //p1指向小数组最小值,p2指向大数组最大值,cnt记录当前两个数组差值
        int p1 = 1, p2 = 6, res = 0, cnt = s2 - s1;
        while(cnt) {
        	//如果走到头了,即小数组所有数都变为6,大数组所有数都变成1,还不够,说明不可能
            if(p1 >= 6 || p2 <= 1) break;
            //如果cnt > 0, 且p1这个位置有数
            if(cnt && q1[p1]) {
            	//如果所有的p1都变成6,还是不足以将cnt填平
                if(cnt > q1[p1] * (6 - p1)) {
                    cnt -= q1[p1] * (6 - p1);
                    res += q1[p1];
                    q1[6] += q1[p1];
                } else {
                	//如果可以把cnt填平,那么只需要cnt / (6 - p1)上取整这么多次就可以
                    int t = (cnt + 5 - p1) / (6 - p1);
                    cnt = 0;
                    res += t;
                    q1[p1] -= t;
                }
            }
            p1++;
			//同理
            if(cnt && q2[p2]) {
                if(cnt > q2[p2] * (p2 - 1)) {
                    cnt -= q2[p2] * (p2 - 1);
                    res += q2[p2];
                    q2[p2] = 0;
                } else {
                    int t = (cnt + p2 - 2) / (p2 - 1);
                    cnt = 0;
                    res += t;
                    q2[p2] -= t;
                }
            }
            p2--;
        }

        if(cnt) return -1;
        else return res;
    }
    int minOperations(vector<int>& nums1, vector<int>& nums2) {
        int s1 = 0, s2 = 0, q1[7], q2[7];
        int n = nums1.size(), m = nums2.size();
        memset(q1, 0, sizeof q1);
        memset(q2, 0, sizeof q2);
        for (int i = 0; i < n; i++) {
            s1 += nums1[i];
            q1[nums1[i]]++;
        }
        for (int i = 0; i < m; i++) {
            s2 += nums2[i];
            q2[nums2[i]]++;
        }

        if(s1 == s2) return 0;
        else {
            return check(q1, q2, s1, s2);
        }
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值