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.
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.
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.
終於等到青蛙過河問題了,一顆賽艇。題目中說青蛙如果上一次跳了k距離,那么下一次只能跳k-1, k, 或k+1的距離,那么青蛙跳到某個石頭上可能有多種跳法,由於這道題只是讓我們判斷青蛙是否能跳到最后一個石頭上,並沒有讓我們返回所有的路徑,這樣就降低了一些難度。我們可以用遞歸來做,我們維護一個哈希表,建立青蛙在pos位置和擁有jump跳躍能力時是否能跳到對岸。為了能用一個變量同時表示pos和jump,我們可以將jump左移很多位並或上pos,由於題目中對於位置大小有限制,所以不會產生沖突。我們還是首先判斷pos是否已經到最后一個石頭了,是的話直接返回true;然后看當前這種情況是否已經出現在哈希表中,是的話直接從哈希表中取結果。如果沒有,我們就遍歷余下的所有石頭,對於遍歷到的石頭,我們計算到當前石頭的距離dist,如果距離小於jump-1,我們接着遍歷下一塊石頭;如果dist大於jump+1,說明無法跳到下一塊石頭,m[key]賦值為false,並返回false;如果在青蛙能跳到的范圍中,我們調用遞歸函數,以新位置i為pos,距離dist為jump,如果返回true了,我們給m[key]賦值為true,並返回true。如果結束遍歷我們給m[key]賦值為false,並返回false,參加代碼如下:
解法一:
class Solution { public: bool canCross(vector<int>& stones) { unordered_map<int, bool> m; return helper(stones, 0, 0, m); } bool helper(vector<int>& stones, int pos, int jump, unordered_map<int, bool>& m) { int n = stones.size(), key = pos | jump << 11; if (pos >= n - 1) return true; if (m.count(key)) return m[key]; for (int i = pos + 1; i < n; ++i) { int dist = stones[i] - stones[pos]; if (dist < jump - 1) continue; if (dist > jump + 1) return m[key] = false; if (helper(stones, i, dist, m)) return m[key] = true; } return m[key] = false; } };
我們也可以用迭代的方法來解,用一個哈希表來建立每個石頭和在該位置上能跳的距離之間的映射,建立一個一維dp數組,其中dp[i]表示在位置為i的石頭青蛙的彈跳力(只有青蛙能跳到該石頭上,dp[i]才大於0),由於題目中規定了第一個石頭上青蛙跳的距離必須是1,為了跟后面的統一,我們對青蛙在第一塊石頭上的彈跳力初始化為0(雖然為0,但是由於題目上說青蛙最遠能到其彈跳力+1的距離,所以仍然可以到達第二塊石頭)。我們用變量k表示當前石頭,然后開始遍歷剩余的石頭,對於遍歷到的石頭i,我們來找到剛好能跳到i上的石頭k,如果i和k的距離大於青蛙在k上的彈跳力+1,則說明青蛙在k上到不了i,則k自增1。我們從k遍歷到i,如果青蛙能從中間某個石頭上跳到i上,我們更新石頭i上的彈跳力和最大彈跳力。這樣當循環完成后,我們只要檢查最后一個石頭上青蛙的最大彈跳力是否大於0即可,參見代碼如下:
解法二:
class Solution { public: bool canCross(vector<int>& stones) { unordered_map<int, unordered_set<int>> m; vector<int> dp(stones.size(), 0); m[0].insert(0); int k = 0; for (int i = 1; i < stones.size(); ++i) { while (dp[k] + 1 < stones[i] - stones[k]) ++k; for (int j = k; j < i; ++j) { int t = stones[i] - stones[j]; if (m[j].count(t - 1) || m[j].count(t) || m[j].count(t + 1)) { m[i].insert(t); dp[i] = max(dp[i], t); } } } return dp.back() > 0; } };
參考資料:
https://discuss.leetcode.com/topic/59337/easy-version-java
https://discuss.leetcode.com/topic/59427/share-my-non-recursive-c-solution-with-simple-comments