leetcode之青蛙过河(C++)

参考链接

  1. https://leetcode-cn.com/problems/frog-jump/
  2. https://leetcode-cn.com/problems/frog-jump/solution/qing-wa-guo-he-by-leetcode-solution-mbuo/

题目描述

一只青蛙想要过河。假定河流被等分为若干个单元格,并且在每一个单元格内都有可能放有一块石子(也有可能没有)。青蛙可以跳上石子,但是不可以跳入水中。

给你石子的位置列表stones(用单元格序号升序表示),请判定青蛙能否成功过河(即能否在最后一步跳至最后一块石子上)。

开始时,青蛙默认已站在第一块石子上,并可以假定它第一步只能跳跃一个单位(即只能从单元格 1 跳至单元格 2 )。

如果青蛙上一步跳跃了 k 个单位,那么它接下来的跳跃距离只能选择为 k - 1、k 或 k + 1 个单位。另请注意,青蛙只能向前方(终点的方向)跳跃。
在这里插入图片描述

解题思路

我的思路

当成动态规划问题,“状态”是当前的石子位置和上一步的距离,“选择”是跳到之后的哪一块石子。当前“状态”能否到达终点,取决于下一步后能否到达终点。

我们每到一个位置,就向后搜索下一步石子,如果下一步石子距离当前位置距离不大于上一步距离加1且不小于距离减1,说明可以跳过去,而且后面还可能有合适的石子,接着向后搜索;如果下一步石子距离当前位置大于了上一步距离加1,就不必向后搜索了,因为后面的距离更远。所以每到一个位置,下一步石子可能有几种情况,我们只需要其中一种可以到达终点就可以了,即对它们的结果取或。

官方思路

同样是动态规划,不过技巧比较多。它的状态表的含义是,dp[i][k] 表示青蛙能否达到「现在所处的石子编号」为 i 且「上一次跳跃距离」为 k 的状态。状态转移方程为:

d p [ i ] [ k ] = d p [ j ] [ k − 1 ] ∪ d p [ j ] [ k ] ∪ d p [ j ] [ k + 1 ] dp\left [ i\right ]\left [ k\right ]=dp\left [ j\right ]\left [ k-1\right ]\cup dp\left [ j\right ]\left [ k\right ]\cup dp\left [ j\right ]\left [ k+1\right ] dp[i][k]=dp[j][k1]dp[j][k]dp[j][k+1]

优化技巧有:

  1. 「现在所处的石子编号」为 i 时,「上一次跳跃距离」k 必定满足 k≤i,因为每次跳跃石子编号至少加1,跳跃距离最多加1。如此,状态表的列可以限定在n。
  2. 当第 i 个石子与第 i−1 个石子距离超过 i 时,青蛙必定无法到达终点。

代码

我的思路

class Solution {
public:
    unordered_map<string, bool> mp;
    bool dp(vector<int>& stones, int start, int prev)
    {
        if (start == stones.size() - 1)
        {
            return true;
        }
        if (mp.count(to_string(start) + '_' + to_string(prev)) == 1)
        {
            return mp[to_string(start) + '_' + to_string(prev)];
        }

        bool flag = false;
        for (int i = start + 1; i < stones.size(); i++)
        {
            if ((stones[i] - stones[start]) > prev + 1)
            {
                break;
            }
            if (i == stones.size() - 1 && (stones[i] - stones[start]) < prev - 1)
            {
                break;
            }
            if ((stones[i] - stones[start]) >= prev - 1)
            {
                flag = flag || dp(stones, i, stones[i] - stones[start]);
            }
        }
        mp[to_string(start) + '_' + to_string(prev)] = flag;
        return flag;
    }
    bool canCross(vector<int>& stones) {
        return dp(stones, 0, 0);
    }
};

官方思路

class Solution {
public:
    bool canCross(vector<int>& stones) {
        int n = stones.size();
        vector<vector<int>> dp(n, vector<int>(n));
        dp[0][0] = true;
        for (int i = 1; i < n; ++i) {
            if (stones[i] - stones[i - 1] > i) {
                return false;
            }
        }
        for (int i = 1; i < n; ++i) {
            for (int j = i - 1; j >= 0; --j) {
                int k = stones[i] - stones[j];
                if (k > j + 1) {
                    break;
                }
                dp[i][k] = dp[j][k - 1] || dp[j][k] || dp[j][k + 1];
                if (i == n - 1 && dp[i][k]) {
                    return true;
                }
            }
        }
        return false;
    }
};
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值