LeetCode 45. 跳跃游戏 II

45. 跳跃游戏 II - 力扣(LeetCode)

解法1:(动态规划 + 贪心)

果然代码越短,思路越难。这题用的是动态规划+贪心的思想。首先分析题意我们可以知道,从索引0这个点开始,我们走一步可以到达一些点,最远可以到达的点自然就是0 + nums[0],所以能从0走一步到达的点是不止一个的,它们组成了一个组,下标范围是1 ~ 0 + nums[0],这些下标到达所需的最少步数就是1,这就形成了局部最优,之后我们如法炮制,再去找走两步可以到达哪些点,找之前,我们首先应该明确一下,为什么这样做是对的?会不会存在我们接下来求出的那些最少走两步可以到达的点,它们实际上还有更优的解呢?比如走一步就能到呢?答案是否定的。如果这个点走一步就能到达,我们知道,肯定是从下标0开始走,走一步到达的。但是我们对于下标0求出了一个走一步所能到达的范围,也就是0 + nums[0],如果下标范围没在这个里面,那是肯定走一步是没法到达的,其实不单单走一步能到走两步能到这个例子,后面同样也是如此分析的,这也是一种单调性,我们随着下标越来越大,每个下标对应的最少能到达的步数是只会递增的,这点是很关键的,所以我们可以用动态规划的思想,用前一个状态去更新后一个状态,因为我们知道,走两步可以到达的点,肯定是从那些走一步可以到达的点当中某一个点走一步到达的,所以我们就可以用那些走一步可以达到的的点,去更新出走两步可以到达的店(注意,我说的这些修饰词:走一步、走两步啥的,都是相对于起点,也就下标0而言的)。寻找走两步可以到达的点,肯定是从走一步可到达的点组的右边界的下一个点开始,也就是0 + nums[0] + 1,从这个点开始,我们用双指针思想,第二个指针从走一步的点组的左边界开始枚举,去找走一步的点组中第一个可以走一步到达0 + nums[0] + 1这个下标的点,然后将这个点的值更新为走两步(也就是2),这里可能会有疑问?为什么非要找走一步的点组中的第一个呢?这个其实也不是,理论上单单对于求0 + nums[0] + 1这个下标的最少步数,我们是无所谓的,无论从走一步点组中的哪一点走,反正都是走一步可以到这个点,0 + nums[0] + 1这个下标的最少步数肯定是被更新成2,不管是从哪个点走过来的。但是我们从走一步点组中的点从左边界到右边界枚举,也是有一个目的:确保最优,如果存在更优解会覆盖之前不那么优秀的解。而且枚举每个走一步的点组,因为它们的下标不同嘛,nums[i]也不同,所以每一个点走一步所能到达最远的点都是不一样的。所以用走一步点组的中的这些点去更新出来的点,也是一个点组,这个点组也就是走两步点组了。并且我们保证了走两步点组中的所有点,它们都是最少的步数,它们之中都不存在,明明一步能到的,我们给他更新成了两步(这个之前也分析了),同样也不存在的是,这个走两步点组的右边界的下一个点一定是需要走三步才能到的,也就是说我们把走两步点组的下标范围,也是更新成了最大的范围,不能再大了。所以这样就保证了我们解的最优性。这也是贪心的思想,每一个当前最优的状态的,是从上一个最优的状态更新过来的,上一个是最优状态,再走一步到达的这个点,肯定也是最优状态,这也就是这题动态规划+贪心的魅力所在吧。

当然,我这是文字通俗解释,会特别冗长多余,需要简洁解释的小伙伴可以参考:

LeetCode 45. 跳跃游戏 II - AcWing

class Solution {
public:
    int jump(vector<int>& nums) {
        int n = nums.size();

        vector<int> f(n);

        for(int i = 1, j = 0;i < n; i ++ ) { 
            while( j + nums[j] < i) j ++ ;
            f[i] = f[j] + 1;
        }

        return f[n -1];
    }
};

解法2 : (贪心 + 双指针)

在这里插入图片描述

class Solution {
public:
    int jump(vector<int>& nums) {
        //贪心 + 双指针
        int n = nums.size();
        //维护三个指针,cur表示当前位置,dis表示目前可到达的最远位置,next表示每次枚举途中可以到达的最远位置
        int ans = 0, cur = 0, dis = 0;
        while(dis < n - 1) { 
            //next是每一轮枚举的临时变量,定义在里面的
            int next = 0;
            while(cur <= dis) {
                next = max(next,cur + nums[cur]);
                cur ++;
            }
            ans ++;
            dis = next;
        }
        return ans;
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值