1.問題の説明:
負でない整数の配列を指定すると、最初は配列の最初の位置にいます。
配列の各要素は、その位置でジャンプできる最大の長さを表します。
最後の位置に到達できるかどうかを判断します。
例1:
入力:[2,3,1,1,4]
出力:true
説明:位置0から位置1に1ステップジャンプしてから、位置1から最後の位置に3ステップジャンプできます。
例2:
入力:[3,2,1,0,4]
出力:false
説明:何があっても、常にインデックス3に到達します。ただし、この位置の最大ジャンプ長は0なので、最後の位置に到達することはできません。
ソース:LeetCode(LeetCode)
リンク:https ://leetcode-cn.com/problems/jump-game
2.思考分析:
①以前にこのトピックについてブログを書いていて、それを解決するためにbfs幅優先検索を使用していて、公式の解決策がコードを解決するためにbfsを使用しないことを含め、誰もこのアイデアを首輪で使用していないことがわかりました最も一般的に使用されるソリューションは、再帰的なバックトラッキング、動的プログラミング、貪欲です。これらの公式ソリューションの中で、これらは私たちの研究に非常に価値があり、非常に貴重です。このような問題を解決するために非常に貴重です。これは役に立ち、理解する必要があります。以下は、公式ソリューションによって提供される方法についての私自身の理解です
②最初の方法は、比較的簡単に再帰を使用することです。これは、タイトルが各ポジションを最大で[i]ステップ右にジャンプできることを示しているため、開始点から開始して現在のポジションから他の位置の場合、試行回数は最大nums [i]です。再帰を使用して解決し、各位置が最後の位置にジャンプできるかどうかを試行し、中間の解中に最後の位置に到達できることがわかった場合は、中間プロセスで直接前の戻り値の再帰とは異なり、途中で戻るだけです。以前は、この再帰プロセスを最終的に完了する前に、すべての回答を解決する必要があります。ここでは、途中で直接戻ることができるものがあることがわかります。問題は異なり、すべての治療方法は異なりますが、一般的に同じです
③2番目の方法は、主に再帰的な試みを利用して、メモリベースの再帰を使用して解決しますが、ここでは配列を使用して中間解の結果を記録します。これは、以前のメモリベースの再帰と基本的に同じです。同じです。以前に解決されていることがわかった場合は、以前に解決された値に直接戻ります。列挙型の3つのマーキングメソッドを使用して、現在の位置が最後の座標にジャンプできるかどうかを示します。プロセス全体は比較的理解しやすいです。公式トップダウンの再帰的ソリューション
④3番目の方法は2番目の方法と似ていますが、最後から2番目の位置から最後まで再帰を使用して、最後の位置に到達できるかどうかを確認します。この位置を良い位置としてマークできる場合、それが不可能な場合は、この位置を悪い位置としてマークしてから、前の位置に進んで現在の位置が以前にマークした良好な位置に到達できるかどうかを確認し、この方法を続け、最後に最初の位置かどうかを確認します最初の位置が適切な位置であれば、右に移動する前にマークされた適切な位置に確実に到達できるため、最終的に初期位置を判断するので、それは適切な位置です。
⑤4番目の方法は貪欲な戦略です。これは理解しやすいが思いつくのが困難です。右側の最後から2番目の位置から試して、現在の位置とジャンプできる最大距離の合計が最後の位置以上かどうかを確認します。到達できるということは、現在の位置が良い位置であることを示します。このとき、左の位置が更新されます。最初の位置まで進むプロセスです。実際、全体の考え方は比較的わかりやすいです。
3.公式コードは次のとおりです。
再帰的なコード:
public class Solution {
public boolean canJumpFromPosition(int position, int[] nums) {
if (position == nums.length - 1) {
return true;
}
int furthestJump = Math.min(position + nums[position], nums.length - 1);
for (int nextPosition = position + 1; nextPosition <= furthestJump; nextPosition++) {
if (canJumpFromPosition(nextPosition, nums)) {
return true;
}
}
return false;
}
public boolean canJump(int[] nums) {
return canJumpFromPosition(0, nums);
}
}
メモリの再帰:
enum Index {
GOOD, BAD, UNKNOWN
}
public class Solution {
Index[] memo;
public boolean canJumpFromPosition(int position, int[] nums) {
if (memo[position] != Index.UNKNOWN) {
return memo[position] == Index.GOOD ? true : false;
}
int furthestJump = Math.min(position + nums[position], nums.length - 1);
for (int nextPosition = position + 1; nextPosition <= furthestJump; nextPosition++) {
if (canJumpFromPosition(nextPosition, nums)) {
memo[position] = Index.GOOD;
return true;
}
}
memo[position] = Index.BAD;
return false;
}
public boolean canJump(int[] nums) {
memo = new Index[nums.length];
for (int i = 0; i < memo.length; i++) {
memo[i] = Index.UNKNOWN;
}
memo[memo.length - 1] = Index.GOOD;
return canJumpFromPosition(0, nums);
}
}
動的計画:
enum Index {
GOOD, BAD, UNKNOWN
}
public class Solution {
public boolean canJump(int[] nums) {
Index[] memo = new Index[nums.length];
for (int i = 0; i < memo.length; i++) {
memo[i] = Index.UNKNOWN;
}
memo[memo.length - 1] = Index.GOOD;
for (int i = nums.length - 2; i >= 0; i--) {
int furthestJump = Math.min(i + nums[i], nums.length - 1);
for (int j = i + 1; j <= furthestJump; j++) {
if (memo[j] == Index.GOOD) {
memo[i] = Index.GOOD;
break;
}
}
}
return memo[0] == Index.GOOD;
}
}
貪欲:
public class Solution {
public boolean canJump(int[] nums) {
int lastPos = nums.length - 1;
for (int i = nums.length - 1; i >= 0; i--) {
if (i + nums[i] >= lastPos) {
lastPos = i;
}
}
return lastPos == 0;
}
}