昨天把N皇后、解数独问题跳过去了,等二刷再去做,今天开始贪心算法部分,开冲!!!
卡哥忠告:
(1)贪心算法其实就是没有什么规律可言,所以大家了解贪心算法 就了解它没有规律的本质就够了;
(2)不用花心思去研究其规律, 没有思路就立刻看题解;
(3)基本贪心的题目有两个极端,要不就是特简单,要不就是死活想不出来;
(4)学完贪心之后再去看动态规划,就会了解贪心和动规的区别。
贪心的理论基础
1.1 题目大纲:
1.2 什么是贪心?
贪心的本质就是选择每一阶层的局部最优,从而达到全局最优。
贪心的例子:有一堆钞票,若可以拿走十张,如何达到最大金额?可以每次都拿最大的,最终结果就是拿走最大数额的钱。
动规的例子:如果有一堆盒子,如果有个背包体积为n,尽可能地把背包装满,此时贪心就不行了,需要动态规划。
1.3 什么时候用贪心?
1、我们在做贪心题目的时候,有没有套路一看这个就是贪心? 贪心算法并没有固定的套路,说白了就是常识性推到 + 举反例。
2、如何看出局部最优是否能推出整体最优呢?有没有什么固定的策略或者套路呢(就像回溯算法一样)? 也没有,靠自己手动模拟下,如果模拟可行,就可以试下贪心策略,如果不可行,则采用动态规划。
3、如何验证可不可以用贪心算法呢? 最好用的就是举反例,如果想不到反例,那就可以试一下贪心
1.4 贪心一般的解题步骤
贪心算法一般分为如下四步(很鸡肋):
- 将问题分解为若干个子问题;
- 找出适合的贪心策略;
- 求解每个子问题的最优解;
- 将局部最优解堆叠成全局最优解
需要重点思考的:每阶段的局部最优是什么?局部最优是否可以推导出全局最优?
题目一:455.分发饼干
Leetcode题目:【455.分发饼干】
参考:【代码随想录之分发饼干】
思路: 此处代码的贪心思路非常简单,就是首先需要两个数组排个序,按照大饼干去喂胃口比较大的孩子的策略,每步都这样做就可以达到全局最优。
class Solution {
public:
int findContentChildren(vector<int>& g, vector<int>& s) {
sort(g.begin(), g.end());
sort(s.begin(), s.end());
int result = 0;
int index = s.size() - 1;
for(int i = g.size() - 1; i>=0; i--){
if(index >= 0 && s[index] >= g[i]){
result++;
index--;
}
}
return result;
}
};
此处还有个C++的语法问题,如果对数组进行逆向排序(从大到小排序),此处需要注意cmp修饰的一定是静态的,因为如果不是静态的话,编译器在编译的时候会自动带上this-> cmp,而sort是库函数提供的,不能是认为专属的某一类修饰的东西,所以需要定义成静态的。
// 只需要定义个这样的函数
static bool cmp(int x, int y){
return x > y;
}
sort(s.begin(), s.end(), cmp);
题目二:376.摆动序列
Leetcode题目:【376.摆动序列】
参考:【代码随想录之摆动序列】
这道题目做起来还是比较难的,需要思考的点:
(1)序列特殊个数为0、1、2情况下,直接返回序列的长度值即可;
(2)如何实现正负数摆动序列的操作,只需要定义两个变量一个prediff,一个curdiff;当prediff * curdiff < 0,说明两者异号,满足情况
(3)平坡情况:
- 首尾元素如何计算?不能用result = 2,首位直接算进去,因为如果序列是[1,1,1,1,1],返回的应该是1,而非2;所以result = 1,然后最后一位可以不进行判断;
- 上下坡出现平坡:考虑pre = 0,curdiff>0或者curdiff<0,这个也要算进去1次;
- 单调坡出现平坡:也满足pre = 0,cur > 0 或者curdiff<0,但是这个就不能算进去;
针对情况上下坡和单调坡出现的相矛盾的问题,只需要让prediff不是完全随着curdiff走,而是prediff只记录有坡度变化的curdiff的值,也就是跟着result走。
最复杂的摆动序列的情况如下所示:
class Solution {
public:
int wiggleMaxLength(vector<int>& nums) {
if(nums.size() == 1) return 1;
if(nums.size() == 2 && nums[0] == nums[1]) return 1;
if(nums.size() == 2 && nums[0] != nums[1]) return 2;
int result = 1;
int prediff = 0, curdiff = 0;
for(int i = 0; i< nums.size() - 1; i++){
curdiff = nums[i + 1] - nums[i];
if((prediff>=0 && curdiff <0) || (prediff<=0 && curdiff>0)){
result++;
prediff = curdiff;
}
}
return result;
}
};
题目三:53.最大子序和
Leetcode题目:【53.最大子序和】
参考:【代码随想录之最大子序和】
连续和 + 一个负数只能让连续和变小,因此可以利用这一点,可以选取起始位置。终止位置,遇到了最大值做一个统计,然后既可以记录终止位置。
class Solution {
public:
int result = INT_MIN;
int temp = 0;
int maxSubArray(vector<int>& nums) {
if(nums.size()==1) return nums[0];
for(int i = 0; i<nums.size(); i++){
temp += nums[i];
if(temp > result) result = temp;
if(temp < 0){
temp = 0;
}
}
return result;
}
};