双周赛122(模拟、冒泡排序、分组循环、脑经急转弯)

双周赛122

3010. 将数组分成最小总代价的子数组 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 是所有分割方案里的最小总代价。

提示:

  • 3 <= n <= 50
  • 1 <= nums[i] <= 50

模拟

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

3011. 判断一个数组是否可以变为有序

中等

给你一个下标从 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 <= nums.length <= 100
  • 1 <= nums[i] <= 28

方法一:冒泡排序

class Solution {
    public boolean canSortArray(int[] nums) {
        int n = nums.length;
        for(int j = n-1; j > 0; j--){
            for(int i = 0; i < j; i++){
                if(nums[i] < nums[i+1])
                    continue;
                if(Integer.bitCount(nums[i]) == Integer.bitCount(nums[i+1]))
                    swap(nums, i, i+1);
            }
        }
        for(int i = 0; i < n-1; i++){
            if(nums[i] > nums[i+1])
                return false;
        }
        return true;
    }
    
    public void swap(int[] nums, int a, int b){
        int tmp = nums[a];
        nums[a] = nums[b];
        nums[b] = tmp;
    }
}

方法二:分组循环

class Solution {
    /**
    如果每一段的最小值,都大于等于上一段的最大值,那么我们就能把数组排成有序的。
     */
    public boolean canSortArray(int[] nums) {
        int n = nums.length;
        int i = 0, preMax = 0;
        while(i < n){
            int mn = nums[i], mx = mn;
            int ones = Integer.bitCount(mn);
            for(i++; i < n && Integer.bitCount(nums[i]) == ones; i++){
                mn = Math.min(mn, nums[i]);
                mx = Math.max(mx, nums[i]);
            }
            // 这一段的最小值 < 上一段最大值,此时构不成有序
            if(mn < preMax){
                return false;
            }
            preMax = mx;
        }
        return true;
    }
}

3012. 通过操作使数组长度最小

中等

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

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

  • nums 中选择 两个不同 的下标 ij ,满足 nums[i] > 0nums[j] > 0
  • 将结果 nums[i] % nums[j] 插入 nums 的结尾。
  • nums 中下标为 ij 的元素删除。

请你返回一个整数,它表示进行任意次操作以后 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

脑经急转弯

https://leetcode.cn/problems/minimize-length-of-array-using-operations/solutions/2613059/on-nao-jin-ji-zhuan-wan-pythonjavacgo-by-2lea/

class Solution {
    /**
    1. 设 x = min(nums), x % 其它数字y = x, 该操作等价于,移除y (y > x)
    2. 如果数组中的最小值只有一个,我们可以操作只剩下一个属
    3. 如果最小值不止一个,如果能构造出一个小于m = min(nums)的正整数,也可以返回1

    结论:当且仅当nums中有不是m的倍数的数,我们才能构造出一个小于m的正整数
        只要数组里面的元素不都是最小元素的倍数,就能使长度变成一
    如果所有数都是m的倍数,可以用m先把大于m的数都移除,然后剩下cnt个m两两一对消除
     */
    public int minimumArrayLength(int[] nums) {
        int m = Integer.MAX_VALUE;
        for (int x : nums) {
            m = Math.min(m, x);
        }

        for (int x : nums) {
            if (x % m > 0) {
                return 1;
            }
        }

        int cnt = 0;
        for (int x : nums) {
            if (x == m) {
                cnt++;
            }
        }
        return (cnt + 1) / 2;
    }
}

3013. 将数组分成最小总代价的子数组 II

困难

给你一个下标从 0 开始长度为 n 的整数数组 nums 和两个 整数 kdist

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

你需要将 nums 分割成 k连续且互不相交 的子数组,满足 第二 个子数组与第 k 个子数组中第一个元素的下标距离 不超过 dist 。换句话说,如果你将 nums 分割成子数组 nums[0..(i1 - 1)], nums[i1..(i2 - 1)], ..., nums[ik-1..(n - 1)] ,那么它需要满足 ik-1 - i1 <= dist

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

示例 1:

输入:nums = [1,3,2,6,4,2], k = 3, dist = 3
输出:5
解释:将数组分割成 3 个子数组的最优方案是:[1,3] ,[2,6,4] 和 [2] 。这是一个合法分割,因为 ik-1 - i1 等于 5 - 2 = 3 ,等于 dist 。总代价为 nums[0] + nums[2] + nums[5] ,也就是 1 + 2 + 2 = 5 。
5 是分割成 3 个子数组的最小总代价。

示例 2:

输入:nums = [10,1,2,2,2,1], k = 4, dist = 3
输出:15
解释:将数组分割成 4 个子数组的最优方案是:[10] ,[1] ,[2] 和 [2,2,1] 。这是一个合法分割,因为 ik-1 - i1 等于 3 - 1 = 2 ,小于 dist 。总代价为 nums[0] + nums[1] + nums[2] + nums[3] ,也就是 10 + 1 + 2 + 2 = 15 。
分割 [10] ,[1] ,[2,2,2] 和 [1] 不是一个合法分割,因为 ik-1 和 i1 的差为 5 - 1 = 4 ,大于 dist 。
15 是分割成 4 个子数组的最小总代价。

示例 3:

输入:nums = [10,8,18,9], k = 3, dist = 1
输出:36
解释:将数组分割成 4 个子数组的最优方案是:[10] ,[8] 和 [18,9] 。这是一个合法分割,因为 ik-1 - i1 等于 2 - 1 = 1 ,等于 dist 。总代价为 nums[0] + nums[1] + nums[2] ,也就是 10 + 8 + 18 = 36 。
分割 [10] ,[8,18] 和 [9] 不是一个合法分割,因为 ik-1 和 i1 的差为 3 - 1 = 2 ,大于 dist 。
36 是分割成 3 个子数组的最小总代价。

提示:

  • 3 <= n <= 105
  • 1 <= nums[i] <= 109
  • 3 <= k <= n
  • k - 2 <= dist <= n - 2

https://leetcode.cn/problems/divide-an-array-into-subarrays-with-minimum-cost-ii/solutions/2614067/liang-ge-you-xu-ji-he-wei-hu-qian-k-1-xi-zdzx/

  • 10
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值