leetcode 122双周赛 解题思路+代码

这篇具有很好参考价值的文章主要介绍了leetcode 122双周赛 解题思路+代码。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本人水平有限,只做出3道,最后1道放弃。

一.将数组分成最小总代价的子数组 I

给你一个长度为 n 的整数数组 nums 。

一个数组的 代价 是它的 第一个 元素。比方说,[1,2,3] 的代价是 1 ,[3,4,1] 的代价是 3 。

你需要将 nums 分成 3 个 连续且没有交集 的子数组。

请你返回这些子数组的 最小 代价 总和 。

示例 1:

输入:nums = [1,2,3,12]
输出:6
解释:最佳分割成 3 个子数组的方案是:[1] ,[2] 和 [3,12] ,总代价为 1 + 2 + 3 = 6 。
其他得到 3 个子数组的方案是:

  • [1] ,[2,3] 和 [12] ,总代价是 1 + 2 + 12 = 15 。
  • [1,2] ,[3] 和 [12] ,总代价是 1 + 3 + 12 = 16 。
    示例 2:

输入:nums = [5,4,3]
输出:12
解释:最佳分割成 3 个子数组的方案是:[5] ,[4] 和 [3] ,总代价为 5 + 4 + 3 = 12 。
12 是所有分割方案里的最小总代价。
示例 3:

输入:nums = [10,3,1,1]
输出:12
解释:最佳分割成 3 个子数组的方案是:[10,3] ,[1] 和 [1] ,总代价为 10 + 1 + 1 = 12 。
12 是所有分割方案里的最小总代价。

解题思路

题目要求将原数组分割为三个子数组,每个子数组的第一个元素为该子数组的代价,求分割方案中三个子数组的最小总代价

设定两个指针i和j作为边界分割,三个子数组的区间范围为[0,i-1],[i,j-1],[j,m]
花销的表达式为nums[0]+nums[i]+nums[j]

代码
class Solution {
    public int minimumCost(int[] nums) {
        int n = nums.length;
        int minCost = Integer.MAX_VALUE;
        for (int i = 1; i < n-1; i++) {
            for (int j = i+1; j < n; j++) {
                minCost = Math.min(minCost, nums[0] + nums[i] + nums[j]);
            }
        }
        return minCost;
    }
}

二、判断一个数组是否可以变为有序

题目

给你一个下标从 0 开始且全是 正 整数的数组 nums 。

一次 操作 中,如果两个 相邻 元素在二进制下数位为 1 的数目 相同 ,那么你可以将这两个元素交换。你可以执行这个操作 任意次 (也可以 0 次)。

如果你可以使数组变有序,请你返回 true ,否则返回 false 。

示例 1:

输入:nums = [8,4,2,30,15]
输出:true
解释:我们先观察每个元素的二进制表示。 2 ,4 和 8 分别都只有一个数位为 1 ,分别为 “10” ,“100” 和 “1000” 。15 和 30 分别有 4 个数位为 1 :“1111” 和 “11110” 。
我们可以通过 4 个操作使数组有序:

  • 交换 nums[0] 和 nums[1] 。8 和 4 分别只有 1 个数位为 1 。数组变为 [4,8,2,30,15] 。
  • 交换 nums[1] 和 nums[2] 。8 和 2 分别只有 1 个数位为 1 。数组变为 [4,2,8,30,15] 。
  • 交换 nums[0] 和 nums[1] 。4 和 2 分别只有 1 个数位为 1 。数组变为 [2,4,8,30,15] 。
  • 交换 nums[3] 和 nums[4] 。30 和 15 分别有 4 个数位为 1 ,数组变为 [2,4,8,15,30] 。
    数组变成有序的,所以我们返回 true 。
    注意我们还可以通过其他的操作序列使数组变得有序。
    示例 2:

输入:nums = [1,2,3,4,5]
输出:true
解释:数组已经是有序的,所以我们返回 true 。
示例 3:

输入:nums = [3,16,8,4,2]
输出:false
解释:无法通过操作使数组变为有序。

解题思路

题目要求我们只能对在二进制下数位为1的数量相同的数字之间进行交换,判断数组是否可以变得有序。
初始思路考虑过,构建一个数组表示原始数组元素二进制形式数位为1的数量,构建另一个数组表示排序后的原始数组二进制形式数位为1的数量,并通过Arrays.equals()进行比较。但是,要考虑到特殊情况数位小的数字可能比数位大的数字要大,例如:4的数位为1,3的数位却为2.

最终考虑到题目要求允许交换二进制中1的数目相同的相邻元素,符合动态连通性的定义,考虑通过并查集解决问题。先克隆一个原始数组,并对其进行排序,以获取每个元素应该存放的位置,并将相邻元素间二进制数位为1数量相同的进行合并,合并后,对每个位置上的现有元素和应存放元素进行查找,判断这两个元素是否具有相同的关键元素,从而判断原始元素是否能够通过元素交换交换到正确的位置上。

代码
class Solution {
    public boolean canSortArray(int[] nums) {
        int n = nums.length;
        // nums1 是 nums 的一个副本,用于排序
        int[] nums1 = nums.clone();
        Arrays.sort(nums1);

        // 初始化并查集的数组
        int[] parent = new int[n];
        for (int i = 0; i < n; i++) {
            parent[i] = i;
        }

        // d 是一个映射,用于记录原数组中每个数字的位置
        Map<Integer, Integer> d = new HashMap<>();
        for (int i = 0; i < n; i++) {
            d.put(nums[i], i);
        }

        // 遍历数组,将二进制1的数量相同的相邻元素合并
        for (int i = 1; i < n; i++) {
            if (Integer.bitCount(nums[i]) == Integer.bitCount(nums[i - 1])) {
                union(parent, i, i - 1);
            }
        }

        // 检查排序后的数组是否可以通过交换特定的相邻元素变为原数组的顺序
        for (int i = 0; i < n; i++) {
            if (nums[i] != nums1[i] && find(parent, i) != find(parent, d.get(nums1[i]))) {
                return false;
            }
        }
        return true;
    }

    // find 函数用于查找元素所在集合的代表元素
    private int find(int[] parent, int x) {
        if (parent[x] != x) {
            parent[x] = find(parent, parent[x]);
        }
        return parent[x];
    }

    // union 函数用于合并两个元素所在的集合
    private void union(int[] parent, int x, int y) {
        int fx = find(parent, x);
        int fy = find(parent, y);
        if (fx != fy) {
            parent[fx] = fy;
        }
    }
}
并查集

适用场景:涉及多个元素分组和组间关系的场景。
动态连接问题

  1. 合并(Union):
    将两个符合相同规则的独立集合合并为一个集合。

  2. 查询(Find):
    找到这个集合的"根",便于我们快速检查两个元素是否属于一个集合。

代码模板:

...
// 初始化并查集数组
int[] parent = new int[n];
for(int i = 0;i < n;i++){
	parent[i] = i;// parent[i]表示元素`i`在并查集中的代表元素的位置
}

// find 函数用于查找元素所在集合的代表元素
private int find(int[] parent,int x){
	if(parent[x] != x){
		parent[x] = find(parent,parent[x]);
	}
	return parent[x];
}

// union 函数用于合并两个元素所在的集合
private void union(int[] parent,int x,int y){
	int fx = find(parent,x);
	int fy = find(parent,y);
	if(fx!=fy){
		parent[fx] = fy;
	}
}

三、通过操作使数组长度最小

题目

给你一个下标从 0 开始的整数数组 nums ,它只包含 正 整数。

你的任务是通过进行以下操作 任意次 (可以是 0 次) 最小化 nums 的长度:

在 nums 中选择 两个不同 的下标 i 和 j ,满足 nums[i] > 0 且 nums[j] > 0 。
将结果 nums[i] % nums[j] 插入 nums 的结尾。
将 nums 中下标为 i 和 j 的元素删除。
请你返回一个整数,它表示进行任意次操作以后 nums 的 最小长度 。

示例 1:

输入:nums = [1,4,3,1]
输出:1
解释:使数组长度最小的一种方法是:
操作 1 :选择下标 2 和 1 ,插入 nums[2] % nums[1] 到数组末尾,得到 [1,4,3,1,3] ,然后删除下标为 2 和 1 的元素。
nums 变为 [1,1,3] 。
操作 2 :选择下标 1 和 2 ,插入 nums[1] % nums[2] 到数组末尾,得到 [1,1,3,1] ,然后删除下标为 1 和 2 的元素。
nums 变为 [1,1] 。
操作 3 :选择下标 1 和 0 ,插入 nums[1] % nums[0] 到数组末尾,得到 [1,1,0] ,然后删除下标为 1 和 0 的元素。
nums 变为 [0] 。
nums 的长度无法进一步减小,所以答案为 1 。
1 是可以得到的最小长度。
示例 2:

输入:nums = [5,5,5,10,5]
输出:2
解释:使数组长度最小的一种方法是:
操作 1 :选择下标 0 和 3 ,插入 nums[0] % nums[3] 到数组末尾,得到 [5,5,5,10,5,5] ,然后删除下标为 0 和 3 的元素。
nums 变为 [5,5,5,5] 。
操作 2 :选择下标 2 和 3 ,插入 nums[2] % nums[3] 到数组末尾,得到 [5,5,5,5,0] ,然后删除下标为 2 和 3 的元素。
nums 变为 [5,5,0] 。
操作 3 :选择下标 0 和 1 ,插入 nums[0] % nums[1] 到数组末尾,得到 [5,5,0,0] ,然后删除下标为 0 和 1 的元素。
nums 变为 [0,0] 。
nums 的长度无法进一步减小,所以答案为 2 。
2 是可以得到的最小长度。
示例 3:

输入:nums = [2,3,4]
输出:1
解释:使数组长度最小的一种方法是:
操作 1 :选择下标 1 和 2 ,插入 nums[1] % nums[2] 到数组末尾,得到 [2,3,4,3] ,然后删除下标为 1 和 2 的元素。
nums 变为 [2,3] 。
操作 2 :选择下标 1 和 0 ,插入 nums[1] % nums[0] 到数组末尾,得到 [2,3,1] ,然后删除下标为 1 和 0 的元素。
nums 变为 [1] 。
nums 的长度无法进一步减小,所以答案为 1 。
1 是可以得到的最小长度。

提示:

1 <= nums.length <= 105
1 <= nums[i] <= 109

解题思路

题目要求我们对数组选取下标i,j(nums[i]>0 nums[j]>0),插入nums[i]%nums[j],并且删除nums[i],nums[j]。根据这个规则,求出数组的最小长度。
首先对nums[i]%nums[j]进行分类讨论
① nums[i]<nums[j]
nums[i]%nums[j]=nums[i],最终保留nums[i],剔除nums[j],理解为小数踢大数
② nums[i]>=nums[j]
A.非整除
显然nums[i]%nums[j]<nums[j]<=nums[i],得到一个更小数,这个更小数可以提走所有大数,结果为1(这也是最优情况)
B.整除
如果大数整除小数,生成0,会占位置,因为nums[i]=0不能参与规则,因此考虑小数整除大数。并且分类讨论奇数和偶数个最小值的情况,总结出规律(m+1)//2

对规律进行提炼优化,即考虑遍历所有数并判断是否能够整除最小数,如果不能,结果为1;如果能,再获取最小数的数量,并且利用(m+1)//2计算。文章来源地址https://www.toymoban.com/news/detail-829445.html

代码
class Solution {
    public int minimumArrayLength(int[] nums) {
        int m = findMin(nums);
        for (int num : nums) {
            if (num % m != 0) { // 可以产生新的最小值
                return 1;
            }
        }
        int length = count(nums, m);
        return (length + 1) / 2;  // 向上取整
    }

    // 寻找数组中的最小值
    private int findMin(int[] nums) {
        int minVal = nums[0];
        for (int num : nums) {
            if (num < minVal) {
                minVal = num;
            }
        }
        return minVal;
    }

    // 计算最小值在数组中出现的次数
    private int count(int[] nums, int m) {
        int count = 0;
        for (int num : nums) {
            if (num == m) {
                count++;
            }
        }
        return count;
    }
}

到了这里,关于leetcode 122双周赛 解题思路+代码的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • leetcode第124场双周赛

    给你一个整数数组  nums  ,如果  nums   至少  包含  2  个元素,你可以执行以下操作: 选择  nums  中的前两个元素并将它们删除。 一次操作的  分数  是被删除元素的和。 在确保  所有操作分数相同  的前提下,请你求出  最多  能进行多少次操作。 请你返回按照上述

    2024年02月19日
    浏览(40)
  • 第 107 场LeetCode双周赛

    A 最大字符串配对数目 显然各字符串对 间匹配的先后顺序不影响最大匹配数目, 可以从后往前遍历数组, 判断前面是否有和当前末尾构成匹配的. B 构造最长的新字符串 记忆化搜索: 定义状态 p a a , b b , a b , l a s t p_{aa,bb,ab,last} p aa , bb , ab , l a s t ​ 为剩余三种字符串分别为aa、

    2024年02月11日
    浏览(44)
  • LeetCode---121双周赛---数位dp

    2996. 大于等于顺序前缀和的最小缺失整数 2997. 使数组异或和等于 K 的最少操作次数 2998. 使 X 和 Y 相等的最少操作次数 2999. 统计强大整数的数目 简单的模拟题,只要按照题目的要求去写代码即可,代码如下 这题考异或的性质---相同为0,相异为1,我们只要关心nums的异或和与

    2024年01月22日
    浏览(56)
  • [LeetCode108双周赛&LeetCode353周赛] 学习用记忆化搜索解决 DP 问题

    参考灵神直播和代码 @cache 装饰器的作用:将传入不同参数得到的函数值存储到缓存,避免下次传递相同参数重复计算结果,可用于解决递归函数重复计算问题,比如递归求斐波那契问题。 https://leetcode.cn/problems/maximum-number-of-jumps-to-reach-the-last-index/ 记忆化搜索 dfs(i) 表示以

    2024年02月13日
    浏览(37)
  • LeetCode 双周赛 106(2023/06/10)两道思维题

    本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 加入知识星球提问。 往期回顾:LeetCode 单周赛第 348 场 · 数位 DP 模版学会了吗? T1. 判断一个数是否迷人(Easy) 标签:计数 T2. 找到最长的半重复子字符串(Medium) 标签:同向双指针 T3. 移动机器人(Medi

    2024年02月08日
    浏览(41)
  • LeetCode 双周赛 101,DP/中心位贪心/裴蜀定理/Dijkstra/最小环

    本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问。 大家好,我是小彭。 这周比较忙,上周末的双周赛题解现在才更新,虽迟但到哈。上周末这场是 LeetCode 第 101 场双周赛,整体有点难度,第 3 题似乎比第 4 题还难一些。 2605. 从两个数字数组里生成最

    2023年04月09日
    浏览(46)
  • LeetCode 双周赛 104(2023/05/13)流水的动态规划,铁打的结构化思考

    本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问。 往期回顾:LeetCode 单周赛第 344 场 · 手写递归函数的通用套路 T1. 老人的数目(Easy) 标签:模拟、计数 T2. 矩阵中的和(Medium) 标签:模拟、排序 T3. 最大或值(Medium) 标签:动态规划、前后缀分解

    2024年02月04日
    浏览(58)
  • Leetcode 75——1768.交替合并字符串 解题思路与具体代码【C++】

    1768. 交替合并字符串 - 力扣(LeetCode) 给你两个字符串  word1  和  word2  。请你从  word1  开始,通过交替添加字母来合并字符串。如果一个字符串比另一个字符串长,就将多出来的字母追加到合并后字符串的末尾。 返回  合并后的字符串  。 1 = word1.length, word2.length = 100

    2024年02月07日
    浏览(78)
  • 【每日算法 && 数据结构(C++)】—— 03 | 合并两个有序数组(解题思路、流程图、代码片段)

    An inch of time is an inch of gold, but you can’t buy that inch of time with an inch of gold. An inch of time is an inch of gold, but you can\\\'t buy that inch of time with an inch of gold 给你两个有序数组,请将两个数组进行合并,并且合并后的数组也必须有序 这个题目要求将两个有序数组合并成一个有序数组。在数

    2024年02月11日
    浏览(55)
  • 【每日算法 && 数据结构(C++)】—— 02 | 数组的并交集(解题思路、流程图、代码片段)

    When you feel like giving up, remember why you started. 当你想放弃时,请记住为什么你开始 给你两个数组,请分别求出两个数组的交集和并集 在数学中,我们可以通过交集和并集来描述两个集合之间的关系。 交集(Intersection) :指的是两个集合中共有的元素组成的集合。可以用符号

    2024年02月11日
    浏览(53)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包