• 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     }
  • 相关阅读:
    java 二维码图片生成工具类
    我的PHP学习过程
    js简单表格操作
    在同一个textarea文本框中设置不同的字体
    基础入门--创建项目
    异常处理方式
    2019年度的瞎几把总结
    WebAPI 返回值
    关于背景色
    使用echarts进行画图
  • 原文地址:https://www.cnblogs.com/boring09/p/4231771.html
Copyright © 2020-2023  润新知