算法练习(10) —— Frog Jump

本文介绍了LeetCode中的403题Frog Jump,讨论了青蛙如何通过不同步长跳跃过河。文章分析了问题的重点,提出不一定要使用动态规划,并给出了使用前一个石头的状态来预测后续可能状态的解决方案。最后,作者反思了代码优化的可能性,包括使用map代替set以提高查找效率,并探讨了使用DFS可能带来的性能提升。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

算法练习(10) —— Frog Jump

习题

本题取自 leetcode 中的 Dynamic Programming 栏目中的第403题:
Frog Jump


题目如下:

Description:

A frog is crossing a river. The river is divided into x units and at each unit there may or may not exist a stone. The frog can jump on a stone, but it must not jump into the water.

Given a list of stones’ positions (in units) in sorted ascending order, determine if the frog is able to cross the river by landing on the last stone. Initially, the frog is on the first stone and assume the first jump must be 1 unit.

If the frog’s last jump was k units, then its next jump must be either k - 1, k, or k + 1 units. Note that the frog can only jump in the forward direction.

Example1

[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.

Example2

[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.

Note

  • The number of stones is ≥ 2 and is < 1,100.
  • Each stone’s position will be a non-negative integer < 231.
  • The first stone’s position is always 0.

思路与代码

  • 首先理解题中的几个重点:
    • stones序列是递增的
    • 青蛙不许向后跳
    • 青蛙第一步必须在第0位上跳,且必须跳1步
    • 青蛙上一步跳了k步,这次就只能跳k-1, k, k+1步,三择一。
  • 本题乍一看感觉用动态规划轻轻松松解决,但是实际上写起状态转移方程的时候才感觉到不好表达。因为要考虑的情况太多了。
  • 做了这么多类似的题目,其实也不难发现,能用动态规划做的题目,基本都能用其他算法实现。动态规划有时候感觉就像是一个必能做出的模板,能写出状态转移方程就已经胜利了一半,但是在某些方面未必有其他的算法灵活。所以这次我用了一个更直观简单的方法来解这道题。
  • 因为青蛙是从头开始跳,且不能往回跳,这就意味着前面的跳完之后就彻底跟后面的状态无关。所以我们就可以用前面石头的状态来记录后面石头可能出现的状态。
  • 从起点开始。用steps集合表示在当前所在石头上能跳的步数。
stones = [0,1,2,3,4,8,9,11]

stones[0] = 0, steps[0] = {1}
stones[0] + 1 == stones[1]
所以能够到达stones[1]
那么下一步能走的步数就是12 (0步剔除)
steps[1] = {1, 2}

stones[1] + 1 == stones[2]
stones[1] + 2 == stones[3]
所以能够到达stones[2]和stones[3]
则steps[2] = {1, 2}; steps[3] = {1, 2, 3}

……

每个点的steps集合并不是一开始就完全算出来的。在进行不同跳跃的时候,集合内的数量都有可能在增多。
  • 考虑到set里的数不能重复,所以采用set来构造steps集合数组。
  • 反思:
    • 其实在下面的代码中,主要的问题是如何找到与某一个step匹配的stone,每找一次就要花费<=n的时间开销,代码也显得很复杂。所以我觉得steps改成map可能会更好,用stones数组的每个值作key,这样查找的时候就能一步到位。
    • 另外这种类似于广度优先的方法虽然有效,但是效率不一定是最高。改成dfs的方法运行速度上可能会更上一筹。

具体代码如下:

#include <iostream>
#include <vector>
#include <set>
using namespace std;

class Solution {
public:
    bool canCross(vector<int>& stones) {
        int len = stones.size();
        if (len == 1)
            return true;

        set<int>* steps = new set<int>[len];

        steps[0].insert(1);

        for (int i = 0; i < len - 1; i++) {
            int poss = steps[i].size();
            if (poss == 0)
                continue;

            for (auto it = steps[i].begin(); it != steps[i].end(); it++) {
                int temp = *it + stones[i];
                if (temp == stones[len - 1])
                    return true;
                // 寻找stones数组中是否有值等于temp,即能不能走出这个步数
                int j = i + 1;
                while (j < len && stones[j] <= temp) {
                    if (stones[j] == temp) {
                        // 不能回退
                        if (*it - 1 >= 1)
                            steps[j].insert(*it - 1);
                        steps[j].insert(*it);
                        steps[j].insert(*it + 1);
                        break;
                    }
                    j++;
                }
            }
        }

        if (steps[len - 1].empty())
            return false;
        return true;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值