赞
踩
贪心算法系列:(之前还有一篇文章描述的也是贪心算法:https://blog.csdn.net/Little_ant_/article/details/116098188)
以下摘自百度百科:
贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,算法得到的是在某种意义上的局部最优解。
贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择。
利用贪心法求解的问题应具备如下2个特征 。
1、贪心选择性质
一个问题的整体最优解可通过一系列局部的最优解的选择达到,并且每次的选择可以依赖以前作出的选择,但不依赖于后面要作出的选择。这就是贪心选择性质。对于一个具体问题,要确定它是否具有贪心选择性质,必须证明每一步所作的贪心选择最终导致问题的整体最优解 。
2、最优子结构性质
当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。问题的最优子结构性质是该问题可用贪心法求解的关键所在。在实际应用中,至于什么问题具有什么样的贪心选择性质是不确定的,需要具体问题具体分析(这里指的是采用何种贪心策略) 。
贪心算法不从整体最优上加以考虑,所做出的仅是在某种意义上的局部最优选择。使用贪心策略要注意局部最优与全局最优的关系,选择当前的局部最优并不一定能推导出问题的全局最优。贪心策略解题需要解决以下两个问题:
1、该问题是否适合使用贪心策略求解,也就是该问题是否具有贪心选择性质;
2、制定贪心策略,以达到问题的最优解或较优解。
题目链接:https://leetcode-cn.com/problems/jump-game/
题目描述:给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标。
判断是否能够到达最后一个下标,那如果每次我们都在当前可选的位置区间内选择一个位置,满足从这个位置出发能够走的距离更远(覆盖其他位置的可达处),那么这个位置就是当前的最优选择。而我们尽最大努力一步步跳跃,如果最后也不能到达最后一个下标,那无论如何也到不了了,这个便是最优子结构性质。
如何选择当前最优的位置?以序列 3、5、4、2、9、6、1…作为示例简单说明。初始时所在位置为 0,当前值为 3,所以下一步可跳跃的区间为[1,3];
然后分别在区间下标 1、 2、 3 处判断,如果下一步跳到 1 处,下下一步的区间[2,6],如果下一步跳到 2 处,下下一步的区间为[3,6],如果下一步跳到 3 处,下下一步的区间为[4,5];
此时,因为 3 处能够到达的最远距离只能到 5,它所能到达的位置,1 和 2 都可以到达,那我们在 1 或 2 中随便选一个即可,不会影响最终结果。
假设选 1:那么接下来就会选择下标 4 处… 。选 2 的话接下来也是选择下标 4 …
参考算法如下:
public boolean canJump(int[] nums) { int value,nextI=0; //nextI 表示在当前区间内选择下一跳的位置 for(int i=0;i<nums.length;){ if(i==nums.length-1)//边界条件1 已经到达最后一个位置时返回true return true; value=-1; // 表示 nextI 能够到达的最远位置 int tmp=nums[i];//tmp 表示当前可选区间长度 if(tmp==0&&i!=nums.length-1)//边界条件2 如果长度为 0,并且当前 i 不是最后一个位置时返回false return false; for(int j=i+1;tmp!=0;j++,tmp--){//求区间内的 top 1 if(j>=nums.length-1)//边界条件3 当前区间内有下标超出了最后一个位置时返回true return true; if((nums[j]+j)>value){ //如果有多个相同的最远距离时取第一个, 改为 >= 时取最后一个 value=nums[j]+j; nextI=j; } } i=nextI; } return false; //这个语句不会被执行。 }
上面那个算法过于复杂了,本题其实只需要更新保存最远跳跃距离即可。参考代码如下:
//贪心:维护最远可到达距离,如果它>=最后一个位置,返回true;否则返回false
public boolean canJump(int[] nums) {
int value=0;// 维护最远能够到达的位置,初始值只能为0.
for(int i=0;i<nums.length;i++){
if(i<=value){
value=Math.max(value,i+nums[i]);
if(value>=nums.length-1)
return true;
}
else
return false;
}
return false;//这个语句不会被执行
}
题目链接:https://leetcode-cn.com/problems/jump-game-ii/submissions/
题目描述:给你一个非负整数数组 nums ,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
你的目标是使用最少的跳跃次数到达数组的最后一个位置。
假设你总是可以到达数组的最后一个位置。
上一道题的解法 1 的功能不就是在判断最小的跳跃次数(最优的跳跃位置)能否到达终点嘛,所以将它改一下就好了。边界条件 2 需要去掉,因为本题已经告诉我们一定可以到达最后一个位置。
参考算法如下:
public int jump(int[] nums) { int value,nextI=0,ans=0; //nextI 表示在当前区间内选择下一跳的位置 for(int i=0;i<nums.length;){ if(i==nums.length-1)//边界条件1 已经到达最后一个位置时返回true return ans; value=-1; // 表示 nextI 能够到达的最远位置 int tmp=nums[i];//tmp 表示当前可选区间长度 for(int j=i+1;tmp!=0;j++,tmp--){//求区间内的 top 1 if(j>=nums.length-1)//边界条件3 当前区间内有下标超出了最后一个位置时返回true return ans+1; if((nums[j]+j)>value){ //如果有多个相同的最远距离时取第一个(改为 >= 时取最后一个) value=nums[j]+j; nextI=j; } } i=nextI; ans++; } return 1; //这个语句不会被执行。 }
有一个超时的回溯算法了解一下:
int mn=Integer.MAX_VALUE;
public int jump(int[] nums) {
solve(nums,0,0);
return mn;
}
public void solve(int[] nums,int start,int count){
if(start>=nums.length-1){
mn=Math.min(mn,count);
return;
}
for(int i=start+1,c=nums[start];c>0;i++,c--)
solve(nums,i,count+1);
}
深度优先遍历是回溯算法使用的策略。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。