【LeetCode】1775. 通过最少操作次数使数组的和相等

news2024/11/18 23:31:14

题目描述

给你两个长度可能不等的整数数组 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 <= 105
1 <= nums1[i], nums2[i] <= 6

方法一:我的方法,双指针 + 优化(确保不超时)

思路:

  • 首先对两个数组分别求和,得到 sum1 和 sum2 ,计算得到两者差值的绝对值 diff
  • 然后通过比较确定 「总和较大和较小的数组」, 传入 函数 count ,返回最少操作次数;
  • 在函数 count 中,对两个数组进行排序, 其中总和较大的数组 降序 ,方便减少总和 ;总和较小的数组 升序 ,方便增大总和。
  • 之后开始使用 双指针 遍历两个数组,目标是使得差值 diff 为 0 ,判断当前元素与 1 或 6 的差值,选择差值较大的元素进行变换,以尽快使 diff = 0。
  • 当其中一个数组中能遍历的元素都遍历完之后,就单独遍历剩余的数组;
  • 最后已经完成了所有可能的变换,diff 仍然大于 0 ,那么不可能变换成功,因此 cnt = -1 。
  • 否则返回当前的 cnt 。
  • 该思路会出现 超时 的问题,此时需要进行优化,参考方法二。
    • 假设 nums1 的元素和小于 nums2 的元素和。
    • 把 nums1 的所有数都改为 6 ,nums2 的所有数都改成 1 ,如果 nums1 的元素和仍小于 nums2 的元素和,则说明无论怎么操作,都无法使这两个数组的元素和相等;

情况

  • 优化前,不通过,只通过 58 / 65 的测试点,其余超时。
  • 优化后,能通过;

收获

  • 在处理数组边界的问题上花费了大量时间,以后要更注重下标范围。

时间复杂度:O(n log n)sort 函数的时间复杂度?【不确定】
空间复杂度:O(1)
在这里插入图片描述

class Solution {
public:
    int count(vector<int>& nums_min, vector<int>& nums_max, int diff){
        int cnt = 0;
        int p = 0, q = 0, a = 0, b = 0;
        int len1 = nums_min.size(), len2 = nums_max.size();

        // 优化        .
        // 如果总和较小的数组全变为6,总和较大的数组全变为1
        // 后者仍然大于前者,那么不可能变换成功
        if(len2 > 6 * len1) return -1;

        sort(nums_min.begin(), nums_min.end());
        sort(nums_max.begin(), nums_max.end(), greater<int>());
        while(diff>0){
            if(p==len1 || q==len2 || nums_min[p]==6 || nums_max[q]==1) 
                break;
            a = 6 - nums_min[p];
            b = nums_max[q] - 1;
            if(a > b){
                diff -= a;
                p ++;
            }
            else{
                diff -= b;
                q ++;
            }
            cnt ++;
        }
        // 遍历nums_max
        // 情况1:要么是nums_min全为6,要么是已经遍历到最后一个值
        if(p==len1 || p<len1 && q<len2 && nums_min[p]==6 && nums_max[q]>1){
            while(q < len2 && diff>0){
                b = nums_max[q] - 1;
                diff -= b;
                q ++;
                cnt ++;
            }
        }
        
        // 遍历nums_min
        // 情况1:要么是nums_max全为1,要么是已经遍历到最后一个值
        if(q == len2 ||p<len1 && q<len2 && nums_max[q]==1 && nums_min[p]<6){
            while(p<len1 && diff>0){
                a = 6 - nums_min[p];
                diff -= a;
                p ++;
                cnt ++;
            }
        }    
        if(diff>0) cnt = -1;
        return cnt;
    }
    int minOperations(vector<int>& nums1, vector<int>& nums2) {
        int cnt = 0; // 操作次数
        int sum1 = 0, sum2 = 0;
        // 求和
        for(auto& num : nums1)  sum1 += num;
        for(auto& num : nums2)  sum2 += num;
        int diff = abs(sum1 - sum2);
        // 目标:diff=0
        if(sum1 < sum2) cnt = count(nums1, nums2, diff);
        else cnt = count(nums2, nums1, diff);

        return cnt;
    }
};

方法二:哈希表/数组 + 算法优化

思路:

  • 设 nums1 的元素和 小于 nums2 的元素和(如果不是则交换两个数组),元素和的差为 diff。
  • 那么 nums1 的元素需要变大,nums2 的元素需要变小。
  • 计算每个元素的 最大变化量
    • nums1[i] 最大能变成 6 ,最大变化量为 6 - nums1[i] ;
    • nums2[i] 最小能变成 1 ,最大变化量为 num2[i] - 1;
  • 统计这些变化量的个数, 记录到一个哈希表或长为 6 的数组 cnt 中,也就是有 cnt[i] 个数可以使 diff 减少 i。
  • 从大到小枚举 i = 5,4,3,2,1:
    • 如果 d > i * cnt[i] ,那么应该把这 cnt[i] 个数的变化量拉满, 并更新 diff 为 diff - i * cnt[i];
    • 否则,可以通过需要其中的 ⌈diff / i​⌉ 个数,使 diff 恰好为 0 ,退出循环。
  • 累加需要修改的个数,即为答案,如果无法使 diff = 0,返回 -1 。

优化

  • 假设 nums1 的元素和小于 nums2 的元素和。
  • 把 nums1 的所有数都改为 6 ,nums2 的所有数都改成 1 ,如果 nums1 的元素和仍小于 nums2 的元素和,则说明无论怎么操作,都无法使这两个数组的元素和相等;
  • 对于 nums1 的元素和大于 nums2 的元素和的情况,也同理。
  • 因此,设 n 为 nums1 的长度, m 为 nums2 的长度,我们可以在一开始就判断: 如果「6n < m」 或「6m < n」,直接返回 -1 。否则,一定可以使得两个数组相等,这事因为从「 nums1 的元素和小于 nums2 的元素和」 到 「nums1 的元素和 大于等于 nums2 的元素和」,由于元素值可以变成 [1,6] 的任意值,我们可以每次操作只把一个元素增大 1 或减小 1 ,这样必然会遇到元素和相差为 0 的情况。
    情况
  • 通过;

收获

  • 这个思路很妙,使用了 cnt 数组来记录变化量个数,之后可以通过遍历这个 cnt 数组来确定最小操作次数。
    相比之下我的解法就很麻烦,把每种情况逐一考虑,这样子很容易忽视某种情况。
  • 解法二使用了很多 C++ 的函数,比如 accumulate ,可以直接求总和,其中第三个形参是累加的初值。
  • 解法二直接使用了 swap 函数交换两个数组,确保数组 nums1 的总和一定小于 nums2。省略了 if 的比较。而我的方法则是确定总和较小和较大的数组,传入 count函数中。

时间复杂度:O(m+n),其中 m 和 n 分别是两个数组的长度;
空间复杂度:O(C),这里 C 为 6 ,即数组 cnt 所使用的空间大小。
在这里插入图片描述

class Solution {
public:
    int minOperations(vector<int>& nums1, vector<int>& nums2) {
        if(6 * nums1.size() < nums2.size() || 6 * nums2.size() < nums1.size())  
        return -1;
        int diff = accumulate(nums2.begin(), nums2.end(), 0) - accumulate(nums1.begin(), nums1.end(), 0);
        if(diff < 0){ // nums1总和较大
            diff = abs(diff);
            swap(nums1, nums2); // 统一使nums1的数变大,nums2的数变小
        }
        vector<int> cnt(6); // 统计每个数的变化量
        for(int x : nums1) ++cnt[6 - x];
        for(int x : nums2) ++cnt[x - 1];
        for(int i = 5, ans = 0;; --i){ 
            // 从大到小枚举最大变化量 5 4 3 2 1
            if(i * cnt[i] >= diff) // 可以让 d 变为 0
                return ans + (diff + i - 1) / i;
            ans += cnt[i]; // 需要所有最大变化量为i的数
            diff -= i * cnt[i];
        }
    }
};

参考资料:

  1. 没想明白?一个动画秒懂!(Python/Java/C++/Go)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/70037.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LaTeX行距以及字体大小

LaTeX行距以及字体大小1、行距2、字体大小1、全局模式2、局部模式1、行距 一&#xff1a;改变全文行距 导言部分前加入\linespread{2.0}即可&#xff0c;代表全文两倍行距。 二&#xff1a;部分段落需要改变行距 \usepackage{setspace} \begin{spacing}{2.0}&#xff08;内…

js_实现网页自动跳转

网页自动跳转实现网页定时自动跳转实现网页定时自动跳转 效果&#xff1a; 10秒后自动跳转到网易云音乐的页面 代码&#xff1a; <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title><style>.box{wi…

什么是 NFT 洗盘交易:洗盘交易背后的原理

2022年12月 CoinGecko 与 Footprint Analytics 联合报告 Data Source&#xff1a;Footprint Analytics 市场上有一种说法是&#xff0c;整个NFT 市场的交易本质是欺诈性的&#xff0c;并且是由洗盘交易者操纵出来的假象。我们经常看到许多 媒体头条 在推动这种说法。毕竟&…

HTML学生个人网站作业设计:宠物网站设计——萌宠有家(5页) HTML+CSS 简单DIV布局宠物介绍网页模板代码 DW学生个人网站制作成品下载

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

动态规划:01背包问题例题(leetcode+cpp实现)

文章目录分割等和子集最后一块石头的重量前情回顾&#xff1a; 动态规划(4)&#xff1a;01背包问题详解 分割等和子集 力扣传送门&#xff1a; https://leetcode.cn/problems/partition-equal-subset-sum/ 题目描述&#xff1a; 给你一个整数数组&#xff0c;将这个数组里的…

论文速读Backbone系列一:点云Transformer结合、PointNet++改进、点云卷积核设计

如有错误&#xff0c;恳请指出。 对一些经典论文进行快速思路整理&#xff0c;以下内容主要关注的是3d点云的backbone设计&#xff0c;包括transformer的应用&#xff0c;卷积核的设计&#xff0c;PointNet网络的改进。 文章目录一、Transformer改进1. 《PCT: Point Cloud Tran…

Wireshark高级特征

1&#xff0c;端点和网络会话 想要让网络通信正常进行&#xff0c;你必须至少拥有两台设备进行数据流的交互。端点&#xff08;endpoint&#xff09;就是指网络上能够发送或接收数据的一台设备。两个端点之间的通信被称之为会话&#xff08;conversation&#xff09;。Wiresha…

Python中如何选择Web开发框架?

Python开发中Web框架可谓是百花齐放,各式各样的web框架层出不穷,那么对于需要进行Python开发的我们来说,如何选择web框架也就变成了一门学问了。本篇文章主要是介绍目前一些比较有特点受欢迎的Web框架,我们可以根据各个Web框架的特性进行选择应用。 Django Django是市面上…

利用Astar算法实现飞行轨迹的三维路径规划(基于Matlab代码实现)

目录 1 概述 1.1研究背景 2 运行结果 3 Matlab代码实现 4 参考文献 1 概述 随着自动化技术的发展,现代航空技术水平有了前所未有的提高,促进了无人机在军事、民用领域的广泛应用。航迹规划技术作为无人机任务规划的关键技术,一直都是无人机领域的一大研究热点。无人机航迹规划是…

Synopsys Sentaurus TCAD系列教程之-- Svisual《一》看图工具

Svisual Svisual作为独立的模块&#xff0c;可以实现Inspect对于节点.plt曲线的查看&#xff0c;也可以实现TecplotSV的.tdr看图功能&#xff0c;还可以进行自动化操作。 当作看图工具使用 - 当作一维曲线看图工具&#xff08;.plt,Inspect) 1.1 打开&#xff1a;Sdevice模块…

[附源码]Python计算机毕业设计SSM基于自组网的空地一体化信息系统(程序+LW)

[附源码]Python计算机毕业设计SSM基于自组网的空地一体化信息系统&#xff08;程序LW) 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff0…

F12抓包简单案例

F12抓包原理&#xff1a;在电脑访问互联网之前&#xff0c;进行包录制 1.谷歌开发者工具&#xff08;F12&#xff09; 元素&#xff08;Elements&#xff09;&#xff1a;页面元素查看分析&#xff0c;web自动化 控制台&#xff08;Console&#xff09;:查看日志 来源&#xf…

c语言---指针进阶(2)--玩转指针

今天内容不多&#xff0c;但都是精华。 1.数组参数和指针参数 2.函数指针 2.1笔试题 3.函数指针数组 1.数组参数和指针参数 例1&#xff1a;一维数组传参 void test(int arr[]) {} void test(int arr[10]) {} void test(int *arr) {}void test2(int *arr2[20]) {} void …

kotlin之声明变量的方式

让我们来看一下java声明变量的方式 Java声明变量方式 类型 变量名 等于 初始化值 String str "999999999"; kotlin声明变量方式 <关键字> <变量名称> : <类型> <初始化值> var sr : String "s…

[附源码]Python计算机毕业设计SSM计算机软考系统的设计与实现(程序+LW)

[附源码]Python计算机毕业设计SSM计算机软考系统的设计与实现&#xff08;程序LW) 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。…

nginx安装及使用(详细版)

一、负载均衡介绍 负载均衡分为硬件负载均衡和软件负载均衡。 硬件负载均衡解决方案是最直接服务器和外部网络间安装负载均衡设备&#xff0c;这种设备我们通常称之为负载均衡器产品有F5、NetScaler等。 软件负载均衡解决方案是指一台或多台服务器响应的操作系统上安装一个或多…

【旋转目标检测】旋转标注工具roLabelImg使用教程

旋转目标检测系列文章:第一篇,学会使用roLabelImg标注工具 本次实验在Windows 10系统下进行, python版本3.6.10(2年前安装的python版本,有点老了) 下载标注工具项目代码: https://github.com/cgvict/roLabelImg 下载代码压缩包到桌面: 解压: 打开Anaconda Prompt终…

动态拉取接口数据

通过工具类动态拉取接口数据 写在前言&#xff1a; 使用工具类拉取接口中的数据&#xff0c;存放在自己的数据库&#xff0c;进行展示。 自己去看工具类中的注释&#xff0c;按照自己的需求去修改。 一、工具类 1、整理需求 想通过工具类把接口数据存放在数据库&#xff…

Feign远程调用

Feign是一个声明式的http客户端&#xff0c;官方地址&#xff1a;https://github.com/OpenFeign/feign Fegin的使用步骤如下&#xff1a; 1&#xff09;引入依赖 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-clou…

Win11开始菜单里面的推荐项目怎么全部关闭教学

Win11开始菜单里面的推荐项目怎么全部关闭教学分享。在Win11系统的开始菜单中&#xff0c;有一些推荐的功能程序。这些程序用户平时并没有使用到&#xff0c;所以想要去将它们进行关闭。那么这些程序如何彻底的关闭显示呢&#xff1f;一起来看看关闭的方法吧。 操作方法&#x…