第四章:如何运用贪心思想玩跳跃游戏

labuladong如何运用贪心思想玩跳跃游戏

有关动态规划的问题,大多是让你求最值的,比如最长子序列,最小编辑距离,最长公共子串等等等

那么贪心算法作为特殊的动态规划也是一样,也一定是让你求个最值。

贪心问题往往可以通过动态规划来解决

这道题就是让求:请问通过题目中的跳跃规则,最多能跳多远?如果能够越过最后一格,返回 true,否则返回 false。

55. 跳跃游戏1

方法1:动态规划

  • dp数组的含义:dp[i]表示在当前位置i时,能到达的最远距离(的索引)(前提是能到达当前位置
  • 状态转移方程:当能到达当前位置时,dp[i] = max(dp[i - 1], i + nums[i])。也就是前一个位置能到达的最远距离和当前位置能到达的最远距离的最大值
class Solution(object):
    def canJump(self, nums):
        n=len(nums)
        dp=[0]*n # 构建dp数组
        dp[0]=nums[0]
        for i in range(1,n):
            if dp[i-1]<i: # 到达不了当前位置
                return False
            else: # 可以到达i处,可以进行状态转移
                dp[i] = max(dp[i - 1], i + nums[i])
        return True

方法2:贪心算法

贪心作为一种特殊的动态规划,通常也是用于求最值。在本题,函数的结果并不需要返回dp数组中的值。所以可以用一个全局变量cur来代替dp数组的含义。为了求能到达的最远距离,我们只要在遍历过程中更新并维护这个cur距离即可。

  • cur的含义:表示遍历到当前位置i时,能到达的最远距离(的索引)(前提是能到达当前位置
  • 在for循环中,每一步都计算一下从当前位置最远能够跳到哪里,然后和一个全局最优的最远位置cur做对比,通过每一步的最优解,更新全局最优解,这就是贪心
class Solution(object):
    def canJump(self, nums):
        """
        :type nums: List[int]
        :rtype: bool
        """
        cur=0
        for i in range(len(nums)):
            if cur<i: # 当前的“能量”不足以跳到i处
                return False
            cur = max(cur, i + nums[i]) # 可以跳到i处,这时更新当前“能量”
        return True

45. 跳跃游戏2

方法1:动态规划

因为该题属于求极值问题,所以可以用动态规划的方法解决

  • dp[i]数组的含义:到达位置i时,跳跃的最小次数
  • base case:先将dp数组设为最大值float("inf"),dp[0]=0(第0个位置不用跳)
  • 状态转移方程:从i前面的位置开始顺序遍历,遍历过程中,如果i前面的某一个位置k处的“能量”能跳到i处(nums[k]+k>=i),则比较已保存的dp[i]和dp[k]+1,取二者的最小值 (始终使dp[i]保持最小值)

类似的题目有300. 最长递增子序列,也是在两层for循环中,用if条件进行判断,从而(通过状态转移来)更新dp数组

class Solution(object):
    def jump(self, nums):
        dp = [float("inf") for _ in range(len(nums))]
        dp[0]=0
        for i in range(1, len(nums)):
            for k in range(i):
                if nums[k]+k >= i :
                    dp[i]=min(dp[i],dp[k]+1)
        return dp[-1]
using namespace std;
class Solution {
public:
    int jump(vector<int> &nums) {
        int n = nums.size();
        vector<int> dp(n, INT_MAX);
        dp[0]=0;
        for (int i = 1; i < n; ++i) {
            for (int j = 0; j < i; ++j) {
                if(nums[j]+j>=i){
                    dp[i]=min(dp[i],dp[j]+1);
                }
            }
        }
        return dp.back();
    }
};

方法2:贪心算法(供参考)

在for循环中,farthest表示(从当前第i个位置)能到达最远距离的下标

  • 我们不需要【递归地】计算出所有选择的具体结果然后比较求最值,而只需要做出那个最有【潜力】,看起来最优的选择即可。
  • i 和 end 标记了可以选择的跳跃步数,farthest 标记了所有选择 [i..end] 中能够跳到的最远距离,step记录了跳跃次数。
  • 当i到达end处时,把end更新为更远的farthest,步数+1
class Solution(object):
    def jump(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        if not nums: return 0
        farthest,end,step=0,0,0
        for i in range(len(nums)-1):
            farthest=max(farthest,i+nums[i])
            if i==end:
                end=farthest
                step+=1
        return step

   转载规则


《第四章:如何运用贪心思想玩跳跃游戏》 M 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
第四章:如何k个一组反转链表 第四章:如何k个一组反转链表
labuladong如何k个一组反转链表 25. K 个一组翻转链表 利用栈先进后出,反转k个一组的链表 stack中存放的是ListNode 虽然存放在stack中,但ListNode之间的指向关系仍然保留 以 1->2-&
2020-07-13
下一篇 
基类派生类及动态绑定 基类派生类及动态绑定
使用基类的引用或者指针调用一个虚成员函数时才会执行动态绑定 定义基类和派生类定义基类class Quote{ private: string bookNo; protected: double price=0; publ
2020-07-09
  目录