题目是LeetCode2020春季全国编程大赛个人赛的第四题,链接:LCP 09. 最小跳跃次数。具体描述见原题。
这道题是hard难度的,先讲一种当时竞赛时想到的做法,就是用一个arrived
数组记录在第count
步所能到达的位置遍历数组的过程中,发现当前位置i
可以跳出终点,则将arrived[i]
置为true
,如果当前位置已经是arrived[i]==true
了,则此后所有位置都可以置为true
了(因为后面这些位置都可以跳回到当前位置)。时间复杂度为
O
(
n
2
)
O(n^{2})
O(n2),空间复杂度为
O
(
n
)
O(n)
O(n)。
JAVA版代码如下:
class Solution {
public int minJump(int[] jump) {
// arrived[i]:从编号i开始可以通过count次越过N-1
boolean[] arrived = new boolean[jump.length];
int count = 0;
int mostLeft = jump.length - 1;
while (true) {
for (int i = 0; i < jump.length; ++i) {
if (arrived[i]) {
++i;
while (i <= mostLeft) {
arrived[i++] = true;
}
break;
}
if (i + jump[i] >= jump.length || arrived[i + jump[i]]) {
arrived[i] = true;
}
}
while (mostLeft >= 0 && arrived[mostLeft]) {
--mostLeft;
}
++count;
if (arrived[0]) {
break;
}
}
return count;
}
}
提交结果如下:
然后又可以用动态规划的方法,假设dp[i]
表示从第i
位跳出N-1
所需要的最小次数,则从右往左遍历,如果有i+jump[i]>=N
,则说明可以一步跳出,所以dp[i]=1
,否则需要跳到i+jump[i]
,所以此时dp[i]=dp[i+jump[i]]+1
。另外得到一个dp[i]
之后,还需要考虑其后面的位置有可能通过跳回i
再跳出N-1
,所以需要遍历后面所有j
直到dp[j]<dp[i]+1
,因此在这个j
后面的都可以通过跳回j
再跳出N-1
,最多需要的次数也只是dp[j]+1<=dp[i]+1
,保证不比跳回i
需要的次数更多。
JAVA版代码如下:
class Solution {
public int minJump(int[] jump) {
// dp[i]:从i跳出N-1需要的最少次数\
int N = jump.length;
int[] dp = new int[N];
for (int i = N - 1; i >= 0; --i) {
if (i + jump[i] >= N) {
dp[i] = 1;
}
else {
dp[i] = dp[i + jump[i]] + 1;
}
for (int j = i + 1; j < N && dp[j] >= dp[i] + 1; ++j) {
// j位置上的可以先跳回到i再通过i跳出N-1,遍历到dp[j] < dp[i] + 1为止
// 因为在这个j之后的dp都可以通过跳到j再跳出N-1,次数(dp[j] + 1 < dp[i] + 1)不会比跳回i更多
dp[j] = dp[i] + 1;
}
}
return dp[0];
}
}
提交结果如下:
Python版代码如下:
class Solution:
def minJump(self, jump: List[int]) -> int:
N = len(jump)
dp = [0] * N
for i in range(N - 1, -1, -1):
if i + jump[i] >= N:
dp[i] = 1
else:
dp[i] = dp[i + jump[i]] + 1
j = i + 1
while j < N and dp[j] >= dp[i] + 1:
dp[j] = dp[i] + 1
j += 1
return dp[0]
提交结果如下: