【周赛总结】18-周赛353

18-周赛353

卡在了T3,状态定义出现了偏差WA了

找出最大的可达成数字【LC2769】

给你两个整数 numt

如果整数 x 可以在执行下述操作不超过 t 次的情况下变为与 num 相等,则称其为 可达成数字

  • 每次操作将 x 的值增加或减少 1 ,同时可以选择将 num 的值增加或减少 1

返回所有可达成数字中的最大值。可以证明至少存在一个可达成数字。

  • 思路

    x减小1,num增加1,相当于每次操作可以将num增加2,因此答案为 n u m + 2 ∗ t num+2*t num+2t

  • 实现

    class Solution {
        public int theMaximumAchievableX(int num, int t) {
            return num + 2 * t;
        }
    }
    
    • 复杂度
      • 时间复杂度: O ( n ) \mathcal{O}(n) O(n)
      • 空间复杂度: O ( 1 ) \mathcal{O}(1) O(1)

达到末尾下标所需的最大跳跃次数【LC2770】

给你一个下标从 0 开始、由 n 个整数组成的数组 nums 和一个整数 target

你的初始位置在下标 0 。在一步操作中,你可以从下标 i 跳跃到任意满足下述条件的下标 j

  • 0 <= i < j < n
  • -target <= nums[j] - nums[i] <= target

返回到达下标 n - 1 处所需的 最大跳跃次数

如果无法到达下标 n - 1 ,返回 -1

  • 思路:dp枚举选哪个

    定义 d p [ i ] dp[i] dp[i]为跳跃至下标 i i i时的最大次数,那么 i i i处的最大跳跃次数为能跳跃至其的位置的最大次数+1

    • 状态转移:枚举 i i i可以从哪个 j j j跳跃而来
      d p [ i ] = m a x ( d p [ j ] ) + 1 dp[i]=max(dp[j])+1 dp[i]=max(dp[j])+1

    • 初始化

      dp[0]=0;

  • 实现

    class Solution {
        public int maximumJumps(int[] nums, int target) {
            // 最大跳跃次数:每次跳跃尽可能少跳
            int n = nums.length;
            int[] dp = new int[n];// 跳跃至i时的最大次数
            Arrays.fill(dp, -1);
            dp[0] = 0;
            for (int i = 1; i < n; i++){
                // 向前搜索可以跳跃的最近位置
                for (int j = 0; j < i; j++){
                    if (j >= 0 && Math.abs(nums[i] - nums[j]) <= target && dp[j] >= 0){
                        dp[i] = Math.max(dp[i], dp[j] + 1);
                    }
                }
                
            }
            return dp[n - 1];
    
    
        }
    }
    
    • 复杂度
      • 时间复杂度: O ( n 2 ) \mathcal{O}(n^2) O(n2)
      • 空间复杂度: O ( n ) \mathcal{O}(n) O(n)

构造最长非递减子数组【LC2771】

给你两个下标从 0 开始的整数数组 nums1nums2 ,长度均为 n

让我们定义另一个下标从 0 开始、长度为 n 的整数数组,nums3 。对于范围 [0, n - 1] 的每个下标 i ,你可以将 nums1[i]nums2[i] 的值赋给 nums3[i]

你的任务是使用最优策略为 nums3 赋值,以最大化 nums3最长非递减子数组 的长度。

以整数形式表示并返回 nums3最长非递减 子数组的长度。

注意:子数组 是数组中的一个连续非空元素序列。

  • 错误思路

    dp的同时稍微贪心了一下,比如根据大小判断应该取哪个nums…

  • 思路

    每次选择的数可以是nums1,也可以是nums2,其前一个数字也有两种选择,因此需要定义两种状态记录以nums1/nums2[i]结尾的最长非递减 子数组的长度

    • 状态定义:

      • d p [ i ] [ 0 ] dp[i][0] dp[i][0]nums1[i]结尾的最长非递减 子数组的长度
      • d p [ i ] [ 1 ] dp[i][1] dp[i][1]nums2[i]结尾的最长非递减 子数组的长度
    • 状态转移:

      • 考虑以nums1[i]结尾时

        • 如果 n u m s 1 [ i ] ≥ n u m s 1 [ i − 1 ] nums1[i]\ge nums1[i-1] nums1[i]nums1[i1],那么 d p [ i ] [ 0 ] dp[i][0] dp[i][0]可以由 d p [ i − 1 ] [ 0 ] + 1 dp[i-1][0]+1 dp[i1][0]+1转移而来

        • 如果 n u m s 1 [ i ] ≥ n u m s 2 [ i − 1 ] nums1[i]\ge nums2[i-1] nums1[i]nums2[i1],那么 d p [ i ] [ 0 ] dp[i][0] dp[i][0]可以由 d p [ i − 1 ] [ 1 ] + 1 dp[i-1][1]+1 dp[i1][1]+1转移而来

        • 如果都不满足,那么 d p [ i ] [ 0 ] dp[i][0] dp[i][0]只能为1

        三者取较大值返回

      • nums1[i]结尾时,同理

    • 状态初始化为1

  • 实现

    class Solution {
        public int maxNonDecreasingLength(int[] nums1, int[] nums2) {
            int n = nums1.length;
            int[][] dp = new int[n][2];
            dp[0][0] = 1;
            dp[0][1] = 1;
            int res = 1;
            for (int i = 1; i < n; i++){
                dp[i][0] = 1;
                dp[i][1] = 1;
                if (nums1[i] >= nums1[i - 1]){
                    dp[i][0] = Math.max(dp[i - 1][0] + 1, dp[i][0]);
                }
                if (nums1[i] >= nums2[i - 1]){
                    dp[i][0] = Math.max(dp[i - 1][1] + 1, dp[i][0]);
                }
                if (nums2[i] >= nums1[i - 1]){
                    dp[i][1] = Math.max(dp[i - 1][0] + 1, dp[i][1]);
                }
                if (nums2[i] >= nums2[i - 1]){
                    dp[i][1] = Math.max(dp[i - 1][1] + 1, dp[i][1]);
                }
                res = Math.max(res, Math.max(dp[i][0], dp[i][1]));
            }
            return res;
        }
    }
    
    • 复杂度
      • 时间复杂度: O ( n ) \mathcal{O}(n) O(n)
      • 空间复杂度: O ( n ) \mathcal{O}(n) O(n)

使数组中的所有元素都等于零【LC2772】

给你一个下标从 0 开始的整数数组 nums 和一个正整数 k

你可以对数组执行下述操作 任意次

  • 从数组中选出长度为 k任一 子数组,并将子数组中每个元素都 减去 1

如果你可以使数组中的所有元素都等于 0 ,返回 true ;否则,返回 false

子数组 是数组中的一个非空连续元素序列。

边减边计算原值判断

  • 思路

    • 如果 n u m s [ i ] > 0 nums[i]>0 nums[i]>0,要使 n u m s [ i ] nums[i] nums[i]变为0,需要对区间 [ i , i + k − 1 ] [i,i+k-1] [i,i+k1]中每个元素都减去 n u m s [ i ] nums[i] nums[i]

      • 对子数组中每个元素加减x,可以考虑使用差分数组
        d [ i ] = a [ i ] − a [ i − 1 ] d[i]=a[i]-a[i-1] d[i]=a[i]a[i1]

      • 相当于对 d [ i ] d[i] d[i] x x x,对 d [ i + k ] d[i+k] d[i+k] x x x

    • 进行操作后, n u m s ′ [ i ] nums'[i] nums[i]处的真实值为 n u m s [ i ] + 累加差分值 nums[i]+累加差分值 nums[i]+累加差分值

      • 如果 n u m s ′ [ i ] = = 0 nums'[i]==0 nums[i]==0,那么不需要进行操作

      • 如果 n u m s ′ [ i ] < 0 nums'[i]<0 nums[i]<0,由于我们只能进行减操作,因此返回false

      • 如果 n u m s ′ [ i ] > 0 nums'[i]>0 nums[i]>0,我们需要对区间 [ i , i + k − 1 ] [i,i+k-1] [i,i+k1]进行修改

        • i + l − 1 < n i+l-1<n i+l1<n时,才能进行修改

          d [ i ] d[i] d[i] n u m s ′ [ i ] nums'[i] nums[i],对 d [ i + k ] d[i+k] d[i+k] n u m s ′ [ i ] nums'[i] nums[i]

        • 否则直接返回false

  • 实现

    在遍历的过程中,累加差分值,对变化后的数字进行判断,不满足要求则直接返回false

    class Solution {
        public boolean checkArray(int[] nums, int k) {
            int n = nums.length, sumD = 0;
            var d = new int[n + 1];
            for (int i = 0; i < n; i++) {
                sumD += d[i];
                int x = nums[i];
                x += sumD;
                if (x == 0) continue; // 无需操作
                if (x < 0 || i + k > n) return false; // 无法操作
                sumD -= x; // 直接加到 sumD 中nums[i] = d[0] + d[1] + d[i - 1] + d[i]
                d[i + k] += x;
            }
            return true;
        }
    }
    
    作者:灵茶山艾府
    链接:https://leetcode.cn/problems/apply-operations-to-make-all-array-elements-equal-to-zero/solutions/2336744/chai-fen-shu-zu-pythonjavacgojs-by-endle-8qrt/
    来源:力扣(LeetCode)
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    
    • 复杂度
      • 时间复杂度: O ( n ) \mathcal{O}(n) O(n)
      • 空间复杂度: O ( n ) \mathcal{O}(n) O(n)

减完后判断差分数组是否都为0

  • 实现

    • 如果操作后数组中所有值为0,那么差分数组中所有值为0
    • 首先计算原数组的差分数组,对于差分数组中值大于0的每个位置,进行区间修改,最后判断差分数组中是否所有值为0
    class Solution {
        public boolean checkArray(int[] nums, int k) {
            int n = nums.length;
            int[] d = new int[n + 1];
            d[0] = nums[0];
            // 计算差分数组
            for (int i = 1; i < n; i++){
                d[i] = nums[i] - nums[i - 1];
            }
            // 从左到右对差分数组里的每个元素进行操作
            for (int i = 0; i + k <= n; i++){
                if (d[i] > 0){      
                    d[i + k] += d[i];
                    d[i] = 0;
                }else if (d[i] < 0){
                    return false;
                }
            }
            // 检查差分数组中不能进行区间修改的元素是否均为 0
            for (int i = n - k + 1; i < n; i++){
                if (d[i] != 0){
                    return false;
                }
            }
            return true;
        }
    }
    
    • 复杂度
      • 时间复杂度: O ( n ) \mathcal{O}(n) O(n)
      • 空间复杂度: O ( n ) \mathcal{O}(n) O(n)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值