zoukankan      html  css  js  c++  java
  • Leetcode#45 Jump Game II

    原题地址

    最朴素的想法是,对于每个位置,挨个尝试一遍不同的跳法,这样总能找到最优解,最坏情况下A[i]=n,那么时间复杂度为O(n^2)。显然会超时,所以在这个朴素的算法上改进。

    如果用动态规划求解,考虑如何划分子问题。一个很自然的想法是将起跳点为子问题边界,令p[i]表示从第i个位置起跳,到终点所需最小步数,则代价函数为p[i] = 1 + min{p[i + j]}, (1 ≤ j ≤ A[i])。再分析一下时间复杂度,对于每个p[i]需要循环A[i]次,一共有n个p[i],所以最坏情况下时间复杂度为O(n^2),可以看出,动态规划实际上跟朴素算法在时间复杂度上没有区别。

    考虑能否使用贪心法,对于每个位置,尽量往最远的地方跳。显然这样只考虑了1跳的情况,比较"短视",很容易举出反例。

    但是,可以把贪心法改进一下,考虑2跳的情况,即贪心的条件是:当前跳跃+下次跳跃的总距离最远。那这样是不是就可以了呢?答案是可以的。反证法:

    假设当前由贪心法选择的方案不是最优解,那么一定存在一个最优跳法,它所需要的总步数更少,并且它至少在某一个位置下,当前一跳和下一跳(共2次跳跃)跳的不是最远的。考虑那个位置,如果按照贪心策略,那么得到的跳法(2次跳跃)的活动范围完全覆盖了最优解选择的跳法的范围。这样就保证了,贪心法一定不会错过某一个能跳的特别远的好机会。因此把这个位置的的跳法从最优解替换成贪心策略的跳法,总得跳数不会增加。这样总可以在有限的次数内,把最优解完全替换成了贪心法得到的解,且跳数跟最优解一样,这就跟最优解的定义矛盾了。

    看一下贪心法的时间复杂度,虽然在每个起跳位置仍然要枚举A[i]次,但对于每个位置只遍历一遍,所以是O(n)。

    回过头来想想:本题的特点是在每个步骤上,可选择的范围是连续的,从1到A[i]。正是这个条件导致了动态规划过于低效以及可以使用贪心法,A[i]表示在i处只能跳A[i]那么远,就不能用贪心法了,动归也不再是O(n^2)的了。

    转化为代码时,实际上我们关心的只是前两跳最多能跳多远,所以用range保存第1跳能及的范围,next表示在此基础上第2跳能及的范围。当所处位置尚在第1跳范围内时,更新第2跳的范围,当所处位置超过了第1跳的范围,count加1,表示不得不跳一次,同时将第1跳的范围更新成原先第2跳的范围。如此迭代进行下去。

    代码:(PS:i表示当前位置,是从1开始索引的)

     1     int jump(int A[], int n) {
     2         int range = 1; // 第1跳能及的最远范围
     3         int next = 0; // 第2跳能及的最远范围
     4         int i = 1;
     5         int count = 0;
     6 
     7         while (range < n) {
     8             if (i <= range) {
     9                 next = max(next, i + A[i - 1]);
    10                 i++;
    11             }
    12             else {
    13                 count++;
    14                 range = next;
    15             }
    16         }
    17 
    18         return count;
    19     }
  • 相关阅读:
    事务传播机制,搞懂。
    洛谷 P1553 数字反转(升级版) 题解
    洛谷 P1200 [USACO1.1]你的飞碟在这儿Your Ride Is Here 题解
    洛谷 P1055 ISBN号码 题解
    洛谷 P2141 珠心算测验 题解
    洛谷 P1047 校门外的树 题解
    洛谷 P1980 计数问题 题解
    洛谷 P1008 三连击 题解
    HDU 1013 题解
    HDU 1012 题解
  • 原文地址:https://www.cnblogs.com/boring09/p/4231771.html
Copyright © 2011-2022 走看看