55. Jump Game

来源:互联网 发布:程序员的自我修养知乎 编辑:程序博客网 时间:2024/06/16 12:44

Description

Given an array of non-negative integers, you are initially positioned at the first index of the array.
Each element in the array represents your maximum jump length at that position.
Determine if you are able to reach the last index.

For example:
A = [2,3,1,1,4], return true.
A = [3,2,1,0,4], return false.

Analyze

这道题描述简单,但看起来就有点赤鸡啊,后面还有一道hard级的加强版。

首先分析条件:我们的目的地是数组尾部,从样例可以看出,唯一能阻止我们前进的是0。而这些数字代表最大步数,意味着我除了可以一步跳过去,也可以小步走过去。

贪心求解:我现在不知道该采取什么策略,是走是跳,那就先不管我跳的结果好不好(是不是跳完会碰到0),先抛开这些杂念,我能走远一点就走远一点行不行?这样我每次都是在向终点靠拢。——这里体现了贪心思想。

看起来好像有希望能解决这个问题,为了保证它的正确性,我们还需要陆续想清楚以下几个问题,完善这个贪心策略:

  1. 对条件进一步分析:每到一个位置,我都可以选择跳最大步数,或者走小于这个步数。也就是说我可以到达包括跳到的最远点的之前的任意一点。多跳几步是同样的道理,所以我们很快可以得到结论:当前能达到的最远位置,以前的每个点我都可以到达。
  2. 这个算法的积极的一面已经明确了,到了一个位置就看是不是0,不是0就直接往前跳。那是0停住了咋办呢?我要怎样跳得更远呢?那答案只能来自于这个位置前面的点,假如存在一个位置,加上它标注的最大步数,能超过当前的最远位置,那我就又能继续往前了。
  3. 所以怎么从前面的点找更优解呢?我想我们前面跳得太欢太不老实了,所以不跳了试着从前面一步一步走?那走一步还是走两步呢? 不了不了,更好的做法是以退为进,往后撤一脚,一蹬,越过眼前那个0。也就是说,从当前位置往前查找,一看到满足条件的我就跳。
  4. 回退的过程有什么问题吗?可能会遇到另一个0,没关系啊下一个;可能会遇到之前已经走过的点,这样的点正会导向当前的困境所以肯定没用,所以需要避开吗?这个疑虑先等等;可能出现某个点,它的位置加标记步数是在当前需要越过的这个0之前,然后需要我从之前这个点再跳一步越过0的吗?不可能的,因为我倒退的时候之前那个位置已经试过了,它无法让我越过0——那这下简单啦,只需独立地对点考虑一个条件,他的位置加步数超过当前障碍0——满足条件就可以前进,不满足条件的一定不能前进。进一步的,前面的疑虑也已经打消了,因为它不符合条件。

边界情况:经过刚刚的分析我们确定了贪心策略。我们不是在直接回答原问题能不能到,而是在找一个最优解,这是最远能到达哪里的问题。那么一旦最远当前位置可以超过或等于数组尾端,就可以停止并且说能到达。否则的话,在回退的过程中假如一直退到了起点,那我们就可以说到不了。

Code

int pos = 0;while(pos < nums.size() - 1) {    if(nums[pos] > 0) pos += nums[pos];    else {        int i = pos - 1;        for(; i > 0; --i)            if(i + nums[i] > pos) {                pos = nums[i] + i;                break;            }        if(i <= 0) return false; //注意<=, 不要死在[0,1]这种样例上    }}return true;

Summary

真是做题五分钟(大雾),讲题两小时!
最后推敲算法的正确性:承边界情况所述,存在一个能间接回答原问题的最优解——总有能到达的最远位置,那我只需要确保我的贪心算法找到最优解。我采取的行动始终都是在向最优解靠近,而回退到起点与不能继续向前等价,所以我的行动一定能终止于找到最优解,或者说提前结束于到了数组尾端。
讨论区又惊现4行代码(3行也够了啦,哦这么说2行也够了),他的策略是从左到右扫过去的,维护一个最远reach,判断的条件比较巧妙。

原创粉丝点击