心情:这是我第一次正儿八经的遇到贪婪算法!
题目:
给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
你的目标是使用最少的跳跃次数到达数组的最后一个位置。
示例:
输入: [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
方法一:暴力
暴力确实一般来说绝对不会是最佳解法,甚至有的时候会超时,但是我想了想还是要练练,万一哪天做题实在想不出来,用暴力又不会超时呢!
省略代码。。。。。。
方法二:动态规划
描述:用dp[i]记录从i到最后一个位置的最小跳跃次数,从后往前去寻找,假设总共nsize个元素,则dp[nsize -1]=0,dp[nsize - 2]=1(这里实际上有错误,比如当nums[nsize -2]=0),然后再依次往前遍历,当遍历到i时,dp[i] = min(1+dp[i+j]),j是小于nums[i]的任意一个值,则结果就是dp[0]。
代码如下:
int jump(vector<int>& nums) {
int nsize = nums.size();
if(nums.size() == 1) return 0;
int dp[nsize] = {0};
dp[nsize - 1] = 0;
dp[nsize - 2] = 1;
for(int i = nsize - 3;i >= 0;i--){
int sum = nsize;
for(int z = 1; z <= nums[i];z++){
if(i+z >= nsize) break;
sum = min(sum,1+dp[i+z]);
}
dp[i] = sum;
}
return dp[0];
}
这个虽然可以求解,但是超时了,并不是因为nsize太多了,而是循环的点除了nsize还有当前的值nums[i]决定了第二层循环的次数,当很大的时候,可想而知。
方法三:贪心算法
贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的是在某种意义上的局部最优解。
当初在学贪心算法的时候我记得一句话,贪心算法不一定是最优解,所以我每次考虑问题的时候实际上是不会朝着贪心算法去考虑的,但这题仔细品品确实可以用贪心,思路我就借用别人的图,理解很快。
总的来说就是选取的下一跳的距离j,使得j+nums[i+j]最大。
代码如下:
int jump(vector<int>& nums){
int nsize = nums.size();
int i = 0;
int ju = 0;
while(i < nsize-1 ){
int sum = 0;
int nexti = i;
for(int j = 1;j <= nums[i];j++){
if(j+i >= nsize - 1){
nexti = j+i;
break;
}
if(j+nums[j+i] > sum){
sum = j+nums[j+i];
nexti = j+i;
}
}
i = nexti;
ju++;
}
return ju;
}
虽然明白了贪心算法的思路,但是我现在仍然有个疑惑?为什么这个情况下贪心算法能求出最优解呢?我想以后慢慢做题再去理会!