参考链接
- https://leetcode-cn.com/problems/frog-jump/
- 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][k−1]∪dp[j][k]∪dp[j][k+1]
优化技巧有:
- 「现在所处的石子编号」为 i 时,「上一次跳跃距离」k 必定满足 k≤i,因为每次跳跃石子编号至少加1,跳跃距离最多加1。如此,状态表的列可以限定在n。
- 当第 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;
}
};