跳跃游戏(贪心->动态规划)

1.跳跃游戏(55-中)

题目描述:给定一个非负整数数组 nums你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个下标。

示例

输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。

思路:本题只是问是否能达到最后一个下标。最优解贪心:计算每个位置最大的覆盖范围,看能否覆盖最后一个位置,维护一个变量记录最大覆盖位置,遍历数组即可。

注意:遍历范围为最大覆盖范围(不断更新),而不是数组的每个位置!!!

代码

public boolean canJump(int[] nums) {
    int n = nums.length;
    int cover = 0;
    for (int i = 0; i <= cover; i++) {
        cover = Math.max(cover, i + nums[i]);
        if (cover >= n - 1) {
            return true;
        }
    }
    return false;
}

2.跳跃游戏II(45-中)

题目描述:给定一个非负整数数组,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

你的目标是使用最少的跳跃次数到达数组的最后一个位置。

假设你总是可以到达数组的最后一个位置。

示例

输入: [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
     从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。

思路:与上一题不同,本题目标是:覆盖最后一个位置的最少跳跃次数。

贪心策略下一次跳跃在上次能跳到的范围(end)内选择一个能跳的最远的位置作为下次的起跳点 !

注意:在遍历数组时,我们不访问最后一个元素,这是因为在访问最后一个元素之前,我们的边界一定大于等于最后一个位置,否则就无法跳到最后一个位置了。如果访问最后一个元素,在边界正好为最后一个位置的情况下,我们会增加一次「不必要的跳跃次数」,因此我们不必访问最后一个元素。

代码

public int jump(int[] nums) {
    int n = nums.length;
    int end = 0, maxCover = 0;
    int cnt = 0;
    for (int i = 0; i < n - 1; i++) {
        maxCover = Math.max(maxCover, i + nums[i]);
        // 到达上一次最大跳跃的右边界
        if (i == end) {
            end = maxCover;
            cnt++;
        }
    }
    return cnt;
}

3.跳跃游戏III(1306-中)

题目描述:这里有一个非负整数数组 arr,你最开始位于该数组的起始下标 start 处。当你位于下标 i 处时,你可以跳到 i + arr[i] 或者 i - arr[i]位置。

请你判断自己是否能够跳到对应元素值为 0 的 任一 下标处。注意,不管是什么情况下,你都无法跳到数组之外。

示例

输入:arr = [4,2,3,0,3,1,2], start = 5
输出:true
解释:
到达值为 0 的下标 3 有以下可能方案: 
下标 5 -> 下标 4 -> 下标 1 -> 下标 3 
下标 5 -> 下标 6 -> 下标 4 -> 下标 1 -> 下标 3 

思路:使用递归进行求解,dfs。

  • 定义一个访问数组,标记该位置是否遍历过。如果当前节点没访问过,标记为true。如果下一次再访问该点说明进入了循环,直接返回false;
  • 递归终止条件:索引越界,返回false;跳到目标值,返回true
  • 递归基本逻辑如题意所述。

代码

public boolean canReach(int[] arr, int start) {
    int n = arr.length;
    boolean[] visit = new boolean[n];
    return dfs(visit, arr, start);
}

public boolean dfs(boolean[] visit, int[] arr, int start) {
    if (start < 0 || start >= arr.length) {
        return false;
    }
    if (arr[start] == 0) {
        return true;
    }
    // 排除重复循环问题
    if (visit[start]) {
        return false;
    } else {
        visit[start] = true;
    }
    return dfs(visit, arr, start + arr[start]) || dfs(visit, arr, start - arr[start]);
}

4.跳跃游戏IV(1696-中)

题目描述:给你一个下标从 0 开始的整数数组 nums 和一个整数 k 。

一开始你在下标 0 处。每一步,你最多可以往前跳 k 步,但你不能跳出数组的边界。也就是说,你可以从下标 i 跳到 [i + 1, min(n - 1, i + k)] 包含 两个端点的任意位置。

你的目标是到达数组最后一个位置(下标为 n - 1 ),你的 得分 为经过的所有数字之和。

请你返回你能得到的 最大得分 。

示例

输入:nums = [1,-1,-2,4,-7,3], k = 2
输出:7
解释:你可以选择子序列 [1,-1,4,3] ,和为 7 。

思路:很显然本题考查动态规划,我们用dp[i]表示以第i个元素结尾的最大得分,那么很明显,每次我们只需要找到dp[i - 1]、dp[i - 2]、...、dp[i - k]的最大值,然后加上当前元素的大小,就可以得到dp[i]。但是超时。

我们每次都要与前面的值进行比较,比较消耗性能,其实我们只需要之前的最大值。这个过程可以使用优先队列(T239滑动窗口的最大值)进行优化:双端队列queue保存滑动窗口中的最大值索引的同时,把最大值索引的【候补】也装进来。

  • 新的索引i对应的得分dp[i - 1]大于队尾对应数值的元素dp[tail],循环弹出队内元素,否则直接入队。
  • 如果队列头是否已经失效,失效直接弹出队头
  • 记录这时的最大值(最大得分)

代码

// 动态规划:超时 
public int maxResult(int[] nums, int k) {
    int n = nums.length;
    int[] dp = new int[n];
    Arrays.fill(dp, Integer.MIN_VALUE);
    // dp[i]:以i结尾的最大得分
    dp[0] = nums[0];

    for (int i = 1; i < n; ++i) {
        for (int j = Math.max(0, i - k); j < i; ++j) {
            dp[i] = Math.max(dp[i], dp[j]);
        }
        dp[i] += nums[i];
    }
    return dp[n - 1];
}

// 优先队列优化
public int maxResult(int[] nums, int k) {
    int n = nums.length;
    Deque<Integer> queue = new LinkedList<>();
    int[] dp = new int[n];
    dp[0] = nums[0];

    for (int i = 1; i < n; ++i) {
        while (!queue.isEmpty() && dp[i - 1] >= dp[queue.peekLast()]) {
            queue.pollLast();
        } 
        queue.addLast(i - 1);
        // 索引不符合步数范围
        if (queue.peekFirst() < i - k) {
            queue.pollFirst();
        }
        dp[i] = dp[queue.peekFirst()] + nums[i];
    }
    return dp[n - 1];
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容

  • 跳跃游戏VII(dp) 1.题目描述   给你一个下标从 0 开始的二进制字符串 s 和两个整数 minJump ...
    执笔之触阅读 287评论 0 0
  • 今天在刷leetcode的每日一题的时候,发现了一道有意思的题目,题目链接,题目原文如下: 给定一个非负整数数组,...
    hebut_moros阅读 286评论 0 0
  • 标题说明了一切,话不多说,开始正文吧! 跳跃游戏 给定一个非负整数数组,你最初位于数组的第一个位置。 数组中的每个...
    思想永不平凡阅读 856评论 0 4
  • 55. 跳跃游戏 给定一个非负整数数组,你最初位于数组的第一个位置。数组中的每个元素代表你在该位置可以跳跃的最大长...
    李海游阅读 185评论 0 0
  • 今天继续写一道动态规划题目 给定一个非负整数数组,假定你的初始位置为数组第一个下标。 数组中的每个元素代表你在那个...
    小熊_宝宝阅读 756评论 0 0