leetcoder 403. Frog Jump 题解

leetcoder 403. Frog Jump 题解

https://leetcode.com/problems/frog-jump/

题目描述

在一条河(直线)上有一些石子,我们只能在石子上移动,每一次我们只能移动的距离是上一次移动的距离以及其+-1,第一步可以移动的距离是1,问我们最终能否到达最后一个点。

样例

Example 1:
[0,1,3,5,6,8,12,17]

There are a total of 8 stones.
The first stone at the 0th unit, second stone at the 1st unit,
third stone at the 3rd unit, and so on...
The last stone at the 17th unit.
Return true. The frog can jump to the last stone by jumping 
1 unit to the 2nd stone, then 2 units to the 3rd stone, then 
2 units to the 4th stone, then 3 units to the 6th stone, 
4 units to the 7th stone, and 5 units to the 8th stone.
Example 2:
[0,1,2,3,4,8,9,11]Return false. There is no way to jump to the last stone as 
the gap between the 5th and 6th stone is too large.

解题思路

因为每一步我们都需要考虑前一步的步骤,所以我们可以用搜索来进行解决。
所以这题的思路就有两种,DFS(深度优先搜索)和BFS(广度优先搜索)

1.DFS

深搜的思路就是说我们每找到一条可行的路径就搜到底,如果没有得到结果,我们再倒回来,找其他路径,
深搜这种思路我们可以用栈来实现,当然最好的写法还是用到递归(隐式栈)去实现。
此题我们可以用dfs(pos, k)表示到pos这个位置,上一步移动了k距离的可行程度,显然当pos 到达最后一个点的时候时这个值为真, 那么我们遍历pos后面的所有点i,如果有一个点dfs(i, stones[i] - stones[pos])的值是真,那么dfs(pos, k)的值就是真,否则不为真
在递归的时候我们还可以采用一种记忆化搜索的方式去优化这个过程。举个例子,0 1 2 3……这组数据,我们可以0→1→3也可以0→1→2→3,第一种和第二种到3这个位置的时候下一步都可以跳1格或2格,显然这个过程是重复了的,我们可以记录下每一个位置的步数状态来进行优化,避免重复搜索。因为我们需要记录的状态不仅仅是每一个点的位置,还要记录到达这个位置上一步的移动距离,那么想要这个值唯一,我们可以选择讲pos + k*10000作为这个状态的标识(因为pos 的范围是1100,所以这个值作为状态一定是唯一的)。

参考程序

C++

class Solution {
public:
    unordered_map<int, bool> dp;

    bool canCross(vector<int>& stones, int pos = 0, int k = 0) {
        int key = pos + k * 10000;
    
        if (dp.count(key) > 0) {
            return dp[key];
        }
    
        for (int i = pos + 1; i < stones.size(); i++) {
            int step = stones[i] - stones[pos];
            if (step < k - 1) {
                continue;
            }
            if (step > k + 1) {
                return dp[key] = false;
            }
            if (canCross(stones, i, step)) {
                return dp[key] = true;
            }
        }
    
        return dp[key] = (pos == stones.size() - 1);
    }
};


Java

public class Solution {
    HashMap<Integer, Boolean> dp = new HashMap<Integer, Boolean>();
    boolean dfs(int[] stones, int pos, int k) {
        int key = pos + k * 10000;
        if(dp.containsKey(key)) {
            return dp.get(key);
        }
        for(int i = pos + 1; i < stones.length; i++) {
            int step = stones[i] - stones[pos];
            if (step < k - 1) {
                continue;
            }
            if (step > k + 1) {
                dp.put(key, false);
                return false;
            }
            if (dfs(stones, i, step)) {
                dp.put(key, true);
                return true;
            }
        }
        dp.put(key, (pos == stones.length - 1));
        return (pos == stones.length - 1);
    }
    public boolean canCross(int[] stones) {
        return dfs(stones, 0, 0);
    }
}


Python

class Solution(object):
    def canCross(self, stones):
        """
        :type stones: List[int]
        :rtype: bool
        """
        dp = {}
        def dfs(stones, pos, k):
            key = pos + k * 10000;
            if dp.has_key(key):
                return dp[key]
            else:
                for i in range(pos + 1, len(stones)):
                    step = stones[i] - stones[pos]
                    if step < k - 1:
                        continue;
                    if step > k + 1:
                        dp[key] = False
                        return False
                    if dfs(stones, i, step):
                        dp[key] = True
                        return True
            dp[key] = (pos == len(stones) - 1)
            return (pos == len(stones) - 1)
        return dfs(stones, 0, 0)


2.BFS:

广搜的思路是找到当前层下可以走的路径,然后一层一层的搜索。
此题广搜的方法就是每到一个点,就从之前记录的到这个点的移动距离中,得到它能到的下一个点记录下移动距离,然后遍历下一个点。最后我们在遍历到最后一个点之后,查看是否最后一个点有之前的记录即可判断。

参考程序

C++

class Solution {
public:
    unordered_map<int, unordered_set<int> > dp;

    bool canCross(vector<int>& stones) {
        int n = stones.size();
        dp[0].insert(0);
        for(int i=0; i<n; i++) {
            unordered_set<int>::iterator it;
            for(it = dp[stones[i]].begin(); it != dp[stones[i]].end(); it++) {
                if(*it > 0) {
                    if(*it > 1) {
                        dp[stones[i] + *it - 1].insert(*it - 1);
                    }
                    dp[stones[i] + *it].insert(*it);
                }
                dp[stones[i] + *it + 1].insert(*it + 1);
            }
        }
        return dp[stones.back()].size();
    }
};


Java

public class Solution {
    HashMap<Integer, HashSet<Integer> > dp = new HashMap<Integer, HashSet<Integer> >();
    public boolean canCross(int[] stones) {
        int n = stones.length;
        for(int i=0; i<n; i++) {
            dp.put(stones[i], new HashSet<Integer>());
        }
        dp.get(0).add(0);
        for(int i=0; i<n; i++) {
            for(int step: dp.get(stones[i])) {
                if(step > 0) {
                    if(step > 1) {
                        HashSet<Integer> set = dp.get(stones[i] + step - 1);
                        if(set != null) {
                            set.add(step - 1);
                        }
                    }
                    HashSet<Integer> set = dp.get(stones[i] + step);
                    if(set != null) {
                        set.add(step);
                    }
                }
                HashSet<Integer> set = dp.get(stones[i] + step + 1);
                if(set != null) {
                    set.add(step + 1);
                }
            }
        }
        return dp.get(stones[n-1]).size() > 0;
    }
}


Python

class Solution(object):
    def canCross(self, stones):
        """
        :type stones: List[int]
        :rtype: bool
        """
        dp = {pos : set() for pos in stones}
        dp[0].add(0)
        for i in xrange(0, len(stones) - 1):
            for step in dp[stones[i]]:
                for k in xrange(step-1, step+2):
                    if k > 0 and stones[i] + k in dp:
                        dp[stones[i] + k].add(k)
        return len(dp[stones[-1]]) > 0


要点突出(需要理解并掌握)

需要掌握深搜和广搜的原理和使用,学会运用栈和递归配合深搜,以及用集合和队列去配合广搜。

面试官角度分析

能够熟练的运用深搜和广搜才是此题的hire程度

相关题目

M: https://leetcode.com/problems/range-sum-query-2d-immutable
M: https://leetcode.com/problems/counting-bits
M: https://leetcode.com/problems/combination-sum-iv
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值