题意
给一个数组A, A[i] 表示在 i 这个位置最多能移动多远,i.e.在位置 i 最多移动到 A[i] + i 这个位置。问从数组第一个元素开始,最少移动多少次,能够到达数组的最后一个元素?我们假设始终能够到达数组的最后一个元素。
思路
最直接的想法是在位置 i 处,更新 [ i + 1, i + A[i] ] 范围内的最少移动次数。但如果数组A中的元素都很大,这种方法的效率很低,是O(n^2),结果是TLE。
注意到,如果能够在 s 次移动之后到达位置 k, 则任意位置 i,只要 i < k,都可以在最多 s 次移动之后到达。不严谨的证明如下:存在位置 j,满足 j < k,且经过 s-1 次移动能够到达 j(因为我们假设能够用 s 次移动到达 k)。这样在范围 [ j+1, k ] 的位置都可以在 s 次移动之后到达。对那些在 j 之前的位置,用相同的方法可以得知一定能在 s-1 的步数内到达。因此,所有在 k 之前的位置,都能够用最多 s 步到达。
如果已经知道了能在 s 步以内到达 [ 0, k ] 范围内的位置 ( k >= 0),就可以知道在 s+1 步以内能够到达的位置。开一个数组保存当前能到达的最远位置,这可以在O(n)时间内完成。之后模拟着走一遍就行了,总的时间复杂度是 O(n)。
C++代码
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
class Solution
{
public:
int jump(vector<int> &vec)
{
vector<int> far(vec.size()+10, 0);
far[0]=vec[0];
for(int i=1;i<vec.size();i++)
far[i]=max(far[i-1],i+vec[i]);
int ans=0,pos=0;
while(pos<vec.size()-1)
{
ans++;
pos=far[pos];
}
return ans;
}
};
python代码
class Solution:
def jump(self, nums):
far = [nums[0]]
for i in range(1, len(nums)):
far.append(max(far[i-1], i+nums[i]))
pos = 0
ans = 0
size = len(nums)
while pos < size - 1:
ans += 1
pos = far[pos]
return ans