贪心+回溯

当然要多快好省跑到终点~

首先想到便是贪心,只要最后一步能跳跃到终点就算成功。就有了如下方程:

locate + nums[locate] >= nums.size() - 1

当然最后一步到不了终点就失败了吗?显然不可能,那就需要回退到再上一次跳跃,少跳一格咯。联系到回溯的特点,有了如下方程:

for(int i=nums[locate];i>0;--i) jump(nums,locate+i)

加上退出条件,就有了如下代码:

class Solution {
public:
    bool canJump(vector<int>& nums) {
        if(nums.size()==0) return false;
        if(nums.size()==1) return true;
        return jump(nums,0);
    }
 
    bool jump(vector<int>& nums,int locate){
        if(locate>=nums.size()-1) return true;
        if(nums[locate]==0) return false;
        for(int i=nums[locate];i>0;--i){
            if(jump(nums,locate+i)) return true;
        }
        return false;
    }
};

结果很可惜,挂在了最后两个用例上:

行不通考虑下一个方法

BFS

搜索算法解决这类题还是很方便的,实现也很简单,把第一个压入队列,并依次将其能够到达的几个点压入队列。依次类推到所有节点,若下一步能够到达终点就算成功。于是:

while(!que.empty()){
    //取队首
    Node tmp = que.front();
    //依次压入,优先将最远的压入
    for(int i=tmp.b;i>0;--i){
        if(tmp.a+i >= length-1) return true;
        que.push(list[tmp.a+i]);
    }
    que.pop();
}

考虑到BFS的特点,一个点可能被压入数次,加上visit数组记忆搜索:

for(int i=tmp.b;i>0;--i){
    if(tmp.a+i >= length-1) return true;
    if(visit[tmp.a+i]) continue;
    que.push(list[tmp.a+i]);visit[tmp.a+i] = 1;
}

加上所有条件,如下:

class Solution {
public:
    struct Node{
        int a,b;
    };
    bool canJump(vector<int>& nums) {
        int length = nums.size();
        if(length==0) return false;
        if(length==1) return true;
        Node list[length];
        for(int i=0;i<length;++i){
            list[i].a = i;list[i].b = nums[i];
        }
        queue<Node> que;
        que.push(list[0]);
        int visit[length];memset(&visit,0,sizeof(int)*length);
 
        while(!que.empty()){
            Node tmp = que.front();
            for(int i=tmp.b;i>0;--i){
                if(tmp.a+i >= length-1) return true;
                if(visit[tmp.a+i]) continue;
                que.push(list[tmp.a+i]);visit[tmp.a+i] = 1;
            }
            que.pop();
        }
        return false;
    }
};

虽然AC,结果还是不如人意

动态规划

这里的状态转移方程还是比较简单,推导过程就省略,能到达赋为1,不能为0,一直往后遍历即可。

class Solution {
public:
    bool canJump(vector<int>& nums) {
        int length = nums.size();
        int dp[length];
        memset(&dp,0,sizeof(int)*length);
        dp[0] = 1;
        for(int i=0;i<length;++i){
            if(dp[i]){
                for(int j=1;j<=nums[i];++j){
                    if(i+j>=length-1) return true;
                    dp[i+j] = 1;
                }
            }
        }
        return dp[length-1];
    }
};

结果感人:

做一点小变换,把循环赋值省掉,dp[0]=255是为了统一后面的memset:

class Solution {
public:
    bool canJump(vector<int>& nums) {
        int length = nums.size();
        char dp[length];
        memset(&dp,0,sizeof(char)*length);
        dp[0] = 255;
        for(int i=0;i<length;++i){
            if(dp[i]){
                if(i+nums[i]>=length-1) 
                    return true;
                else
                    memset(&dp,1,sizeof(char)*(nums[i]+i+1));
            }
        }
        return dp[length-1];
    }
};

结果提升很大,但是还有更好的办法

题目特殊性
1.考虑到题目特殊性,如果能到达某一格,那么左侧的所有位置,都能到达。
2.数组中的每个元素都大于零,那一定能跳到终点。则题目可以转化为检测遇到的0能否跳过。
这两点都是考虑到了特殊性。不一定能够解决更复杂的情况。


HHXXHGGZ
0 声望0 粉丝

下一篇 »
LeetCode 136^56-I