题意
给一个数组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