您的位置:首页 > 其它

算法练习(10) —— Frog Jump

2017-12-02 17:35 573 查看

算法练习(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]
那么下一步能走的步数就是1、2 (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;
}
};
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  算法 leetcode