LeetCode 第252场周赛(贪心,平方和公式,子序列动态规划)

Posted Zephyr丶J

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode 第252场周赛(贪心,平方和公式,子序列动态规划)相关的知识,希望对你有一定的参考价值。

第252场周赛

又是被自己菜哭的一天

5830. 三除数

题目描述

给你一个整数 n 。如果 n 恰好有三个正除数 ,返回 true ;否则,返回 false 。

如果存在整数 k ,满足 n = k * m ,那么整数 m 就是 n 的一个 除数 。

示例 1:

输入:n = 2
输出:false
解释:2 只有两个除数:1 和 2 。
示例 2:

输入:n = 4
输出:true
解释:4 有三个除数:1、2 和 4 。

提示:

1 <= n <= 104

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/three-divisors
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

恰好有三个…就因为这个错了两次
除了1和自己本身,还有一个除数

class Solution {
    public boolean isThree(int n) {
        boolean flag = false;
        for(int i = 2; i < n; i++){
            if(!flag && n / i * i == n){
                flag = true;
                continue;
            }
            if(flag && n / i * i == n)
                return false;
        }
        return flag;
    }
}

看官解,x = n / i,如果 i 和 x 不相同,那么肯定有两个除数
如果i 和x 相同,只加一个
所以除了1和本身,就是一个平方数才满足条件

5831. 你可以工作的最大周数

题目描述

给你 n 个项目,编号从 0 到 n - 1 。同时给你一个整数数组 milestones ,其中每个 milestones[i] 表示第 i 个项目中的阶段任务数量。

你可以按下面两个规则参与项目中的工作:

每周,你将会完成 某一个 项目中的 恰好一个 阶段任务。你每周都 必须 工作。
在 连续的 两周中,你 不能 参与并完成同一个项目中的两个阶段任务。
一旦所有项目中的全部阶段任务都完成,或者仅剩余一个阶段任务都会导致你违反上面的规则,那么你将 停止工作 。注意,由于这些条件的限制,你可能无法完成所有阶段任务。

返回在不违反上面规则的情况下你 最多 能工作多少周。

示例 1:

输入:milestones = [1,2,3]
输出:6
解释:一种可能的情形是:
​​​​- 第 1 周,你参与并完成项目 0 中的一个阶段任务。

  • 第 2 周,你参与并完成项目 2 中的一个阶段任务。
  • 第 3 周,你参与并完成项目 1 中的一个阶段任务。
  • 第 4 周,你参与并完成项目 2 中的一个阶段任务。
  • 第 5 周,你参与并完成项目 1 中的一个阶段任务。
  • 第 6 周,你参与并完成项目 2 中的一个阶段任务。
    总周数是 6 。
    示例 2:

输入:milestones = [5,2,1]
输出:7
解释:一种可能的情形是:

  • 第 1 周,你参与并完成项目 0 中的一个阶段任务。
  • 第 2 周,你参与并完成项目 1 中的一个阶段任务。
  • 第 3 周,你参与并完成项目 0 中的一个阶段任务。
  • 第 4 周,你参与并完成项目 1 中的一个阶段任务。
  • 第 5 周,你参与并完成项目 0 中的一个阶段任务。
  • 第 6 周,你参与并完成项目 2 中的一个阶段任务。
  • 第 7 周,你参与并完成项目 0 中的一个阶段任务。
    总周数是 7 。
    注意,你不能在第 8 周参与完成项目 0 中的最后一个阶段任务,因为这会违反规则。
    因此,项目 0 中会有一个阶段任务维持未完成状态。

提示:

n == milestones.length
1 <= n <= 105
1 <= milestones[i] <= 109

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-number-of-weeks-for-which-you-can-work
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

我明显记得做过一个两个人拿两个石头相撞,然后剩下一个石头的题
然后到这里又忘了咋做了

第一眼看到还是觉得应该就是两个相互抵消,从大到小排序以后,开始从到小抵消,然后测试用例卡住之后,开始想到底应该怎么抵消。[2,2,2]这种情况怎么处理
然后终于想到,应该是将两个最大的数相互抵消到第三个大的数减1,这样依次往后抵消,最后就能得到正确答案
但是无奈超时了,下面是当时写的代码

class Solution {
    public long numberOfWeeks(int[] milestones) {
        
        int l = milestones.length;
        if(l == 1)
            return 1;
        PriorityQueue<Integer> pq = new PriorityQueue<>((a,b) -> (b-a));
        long sum = 0;
        for(int i = 0; i < l; i++){
            pq.offer(milestones[i]);
            sum += milestones[i];
        }
        long count = 0;
        while(pq.size() > 1){
            int first = pq.poll();
            int sec = pq.poll();
            if(!pq.isEmpty()){
                int top = pq.peek();
                int cut = sec - top + 1;
                first = first - cut;
                sec = sec - cut;
                if(first > 0)
                    pq.offer(first);
                if(sec > 0)
                    pq.offer(sec);   
            }else{
                if(Math.abs(first - sec) > 0)
                    pq.offer(Math.abs(first - sec));
            }
        }
        int lev = pq.isEmpty() ? 0 : pq.poll() - 1;
        return sum - (long)lev;

    }
}

一结束,就去看大佬们怎么想的,结果发现是判断最大值就可以了,不过自己看着那个代码想了半天,也没想起来是怎么贪心的,唉,看答案吧

怎么理解呢?

首先对两个比较的数有个认知
就是max,也就是数组中的最大值;另一个是剩余值rest,也就是sum-max
如果max = rest + 1,那么隔一周安排一个max的任务,那么恰好可以全部安排完成
如果max > rest + 1,那么肯定会有剩余,能连续工作的周数就是2 * rest + 1
最难理解的max<rest + 1,这种情况下,为什么肯定能完成呢?
看了一圈,有两个比较好的解释,一个是插空,一个还是抵消
第一种,有max堵墙,然后其他数从大到小一次插空放在两个墙中间,是肯定可以放下的
第二种,rest中的数相互抵消,抵消到和max一样大,就可以全部被安排了。但是能抵消到max一样大么,因为rest中所有数都是比max小的,所以抵消到只有一个数时,是肯定比max小的,所以有多个数的时候,肯定能和max一样大

找不到原来做的那个题了,记得那个题有更好的解释,找到再来补充

class Solution {
    public long numberOfWeeks(int[] milestones) {
        int l = milestones.length;
        long max = 0;
        long sum = 0;
        for(int i = 0; i < l; i++){
            sum += milestones[i];
            max = Math.max(max, milestones[i]);
        }
        long rest = sum - max;

        if(max >= rest + 1)
            return 2 * rest + 1;
        else
            return sum;
    }
}

5187. 收集足够苹果的最小花园周长

题目描述

给你一个用无限二维网格表示的花园,每一个 整数坐标处都有一棵苹果树。整数坐标 (i, j) 处的苹果树有 |i| + |j| 个苹果。

你将会买下正中心坐标是 (0, 0) 的一块 正方形土地 ,且每条边都与两条坐标轴之一平行。

给你一个整数 neededApples ,请你返回土地的 最小周长 ,使得 至少 有 neededApples 个苹果在土地 里面或者边缘上。

|x| 的值定义为:

如果 x >= 0 ,那么值为 x
如果 x < 0 ,那么值为 -x

示例 1:

输入:neededApples = 1
输出:8
解释:边长长度为 1 的正方形不包含任何苹果。
但是边长为 2 的正方形包含 12 个苹果(如上图所示)。
周长为 2 * 4 = 8 。
示例 2:

输入:neededApples = 13
输出:16
示例 3:

输入:neededApples = 1000000000
输出:5040

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-garden-perimeter-to-collect-enough-apples
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

这个题挺简单的,我当时就是找每一圈的规律,然后一圈圈加起来,注意,用long

class Solution {
    public long minimumPerimeter(long neededApples) {
        long count = 0;
        long idx = 1;
        while(count < neededApples){
            long left = idx;
            long right = idx + idx;
            long temp = (left + right - 1) * (right - left) - left;
            count += (temp + right) * 4;
            idx++;
            //System.out.println(count);
        }
        return (idx - 1) * 2 * 4;
        
    }
}

看大佬题解时发现都是用的二分
要用二分的话,必须知道对于当前边长的正方形,苹果的数目
也就是得找到这样一个公式
设右上角顶点的坐标为(idx,idx)
将一圈分成8段,除了四个角和正方向的四个位置,每一段都是(idx,1)到(idx,idx -1),每一段的苹果数为idx *(idx - 1)+(1+idx - 1) *(idx - 1)/2,在加上四个角的8idx,正上下左右4idx
也就是说,一圈共有苹果12idx^2
这样就相当于知道了递推公式,求通项公式
已知f(n)-f(n-1)= 12
n^2,f(0) = 0,求f(n)
累加法,f(n)-f(0) = 12n ^ 2+12(n-1) ^ 2 + … + 12 * 1 + 0
也就是求1到n的平方和,高中还能记着,现在肯定忘了…答案是n(n+1)(2n+1)/6

注意这里,所有变量都要用long,否则乘起来会有错误

class Solution {
    public long minimumPerimeter(long neededApples) {
        long left = 1;
        long right = 100000;
        while(left < right){
            long mid = (right - left) / 2 + left;
            long temp = 2 * mid * (mid + 1) * (2 * mid + 1);
            if(temp >= neededApples){
                right = mid;
            }else{
                left = mid + 1;
            }
        }
        return 8 * left;
    }
}

5833. 统计特殊子序列的数目

题目描述

特殊序列 是由 正整数 个 0 ,紧接着 正整数 个 1 ,最后 正整数 个 2 组成的序列。

比方说,[0,1,2] 和 [0,0,1,1,1,2] 是特殊序列。
相反,[2,1,0] ,[1] 和 [0,1,2,0] 就不是特殊序列。
给你一个数组 nums (仅 包含整数 0,1 和 2),请你返回 不同特殊子序列的数目 。由于答案可能很大,请你将它对 109 + 7 取余 后返回。

一个数组的 子序列 是从原数组中删除零个或者若干个元素后,剩下元素不改变顺序得到的序列。如果两个子序列的 下标集合 不同,那么这两个子序列是 不同的 。

示例 1:

输入:nums = [0,1,2,2]
输出:3
解释:特殊子序列为 [0,1,2,2],[0,1,2,2] 和 [0,1,2,2] 。
示例 2:

输入:nums = [2,2,0,0]
输出:0
解释:数组 [2,2,0,0] 中没有特殊子序列。
示例 3:

输入:nums = [0,1,2,0,1,2]
输出:7
解释:特殊子序列包括:

  • [0,1,2,0,1,2]
  • [0,1,2,0,1,2]
  • [0,1,2,0,1,2]
  • [0,1,2,0,1,2]
  • [0,1,2,0,1,2]
  • [0,1,2,0,1,2]
  • [0,1,2,0,1,2]

提示:

1 <= nums.length <= 105
0 <= nums[i] <= 2

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/count-number-of-special-subsequences
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

class Solution {
    public static final int MOD = (int)1.0e9 + 7;
    public int countSpecialSubsequences(int[] nums) {
        //感觉给我时间应该能做出来,感觉应该去多看看这种子序列的题了
        //想想怎么搞,还是不会
        
        //定义dp[i][j]为使用 nums[0..i] 中的元素可以组成的类型为 j 的子序列的数目
        //j = 0,表示都是i的子序列
        //j = 1,表示0接着1的子序列
        //j = 2,表示0接着1接着2的子序列
        //当nums[i] = 0时,可以接在0的子序列后面,也可以单独成为一个0子序列
        //nums[i] = 1时,可以接在1的子序列后面,也可以接在0的子序列后面
        //nums[i] = 2时,可以接在1的子序列后面,可以接在2的后面
        int l = nums.length;
        int[][] dp = new int[l][3];

        dp[0][0] = nums[0] == 0 ? 1 : 0;
        dp[0][1] = 0;
        dp[0][2] = 0;
        for(int i = 1; i < l; i++){
            if(nums[i] == 0){
                dp[i][0] = (2 * dp[i - 1][0] + 1) % MOD;
                dp[i][1] = dp[i - 1][1];
                dp[i][2] = dp[i - 1][2];
            }
            if(nums[i] == 1){
                dp[i][1] = (2 * dp[i - 1][1] % MOD + dp[i - 1][0]) % MOD;
                dp[i][0] = dp[i - 1][0];
                dp[i][2] = dp[i - 1][2];
            }
            if(nums[i] == 2){
                dp[i][2] = (2 * dp[i - 1][2] % MOD + dp[i - 1][1]) % MOD;
                dp[i][1] = dp[i - 1][1];
                dp[i][0] = dp[i - 1][0];
            }
        }
        return dp[l - 1][2];
    }
}

以上是关于LeetCode 第252场周赛(贪心,平方和公式,子序列动态规划)的主要内容,如果未能解决你的问题,请参考以下文章

解题报告力扣 第 261 场周赛

Leetcode 第175场周赛 题解(完结)

力扣 第314场周赛 6202. 使用机器人打印字典序最小的字符串难度:中等(栈+贪心)

解题报告力扣 第 280 场周赛

解题报告力扣 第 279 场周赛

解题报告力扣 第 285 场周赛