1005.K次取反后最大化的数组和
本题简单一些,估计大家不用想着贪心 ,用自己直觉也会有思路。
文章里面是直接让数组按照绝对值从大到小来排列,确实,这种方法可以在正向遍历改变符号的时候优先改变绝对值大的负数,而在k是奇数的时候改变绝对值最小的符号,这种贪心的策略是贪绝对值大小,只有绝对值较大者才能对数组总和有显著影响。
值得注意的是,在使用sort函数的时候需要自己写比较函数作为第三个参数,注意比较函数的第一个参数是排列的前者第二个参数是后者,要实现绝对值从大到小排列要求二者绝对值函数的值要前者大于后者。
class Solution {
static bool cmp(int a, int b) {
return abs(a) > abs(b);
}
public:
int largestSumAfterKNegations(vector<int>& A, int K) {
sort(A.begin(), A.end(), cmp); // 第一步
for (int i = 0; i < A.size(); i++) { // 第二步
if (A[i] < 0 && K > 0) {
A[i] *= -1;
K--;
}
}
if (K % 2 == 1) A[A.size() - 1] *= -1; // 第三步
int result = 0;
for (int a : A) result += a; // 第四步
return result;
}
};
134. 加油站
本题有点难度,不太好想,推荐大家熟悉一下方法二
https://programmercarl.com/0134.%E5%8A%A0%E6%B2%B9%E7%AB%99.html
我感觉这道题确实比较难想到,主要是对贪心算法的步骤不太熟练。首先就是将一个问题分成几个小问题,这个题目是将走完整个数组的大问题分成能走到当前遍历到的下标处的小问题,具体也就是如果开始出发的下标处不能走到当前的下标处(也就是走到这汽油变成负数了),那就说明从当前下标以及之前的下标开始,都不能到达当前的下一个下标处,而如果一定存在一个开始处下标能走完整个数组(也就是加的汽油的总和不小于消耗的汽油的总和),那就一定是之后的下标作为开始处才可以实现。
因为既然能走到当前下标就意味着不往下走剩余的汽油一定是不小于0的,而又确实走不到下一个下标处,证明当前这个点增加的汽油一定小于走到下一个点消耗的汽油,并且再消耗了剩余积累的汽油还走不到,那么从当前点作为开始就走不通;那么至于为什么从原定开始处一直到当前点中间的哪些点也不能走得通,因为其实从原定开始点到当前点都是一定汽油剩余量不小于0的,那么中间的任意一个点都一定吃了之前的剩余汽油的红利,就是吃了红利还是没能走到下一个点,那么不吃红利就一定走不到下一个点。
值得一提的是,这道题不能只通过判断当前剩余扽汽油总量来判断选定的起始点是否正确,还要判断是否真的能找到一个起始点能走到原点,否则就少考虑了只有最后一个点是正数的情况,只有当证明了有且只有一个开始位置能走遍全程才能通过贪心算法方法确定。
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
int begin=0;
int total=0;
int cur=0;
for(int i=0;i<gas.size();i++){
cur+=gas[i]-cost[i];
total+=gas[i]-cost[i];
if(cur<0){
cur=0;
begin=i+1;
}
}
if(total<0) return -1;
return begin;
}
135. 分发糖果
本题涉及到一个思想,就是想处理好一边再处理另一边,不要两边想着一起兼顾,后面还会有题目用到这个思路
https://programmercarl.com/0135.%E5%88%86%E5%8F%91%E7%B3%96%E6%9E%9C.html
首先要考虑到对于一个位置的糖果数量需要考虑到它两边的糖果数量和等级差异(例如当前等级比右边的等级高,那得到的糖果数量就必须至少是右边糖果+1,再考虑到左边等级可能会增加),然而是没有办法同时考虑两边的情况的,因为一次遍历只能沿着一个方向,注定没法同时得到它两边准确的糖果数量,所以需要两次遍历,一次针对右边大于左边的情况,一次针对左边大于右边的情况。
以这个顺序为例,第一次遍历(右边大于左边的情况)必须是正向遍历,因为毕竟要用到左边的糖果数量,所以数组必须从左至右依次修正数组,一旦右边大于左边,那右边就等于左边糖果+1;
第二次遍历(左边大于右边的情况)必须是反向遍历,因为也会用到右边的糖果数量(因为是最终的遍历,所以右边的糖果数量必须是是准确值),这样只能反向修正数组,因为要利用到上一次遍历的结果,所以一旦左边大于右边,需要在当前糖果数量和右边糖果数量+1中选择较大者,只有取最大值才能让两次便利的结果都有用。
事实上,这两次遍历的顺序可以随意颠倒,也就是先考虑哪一种情况都可以,但是后一次遍历必须要用到前一次遍历的结果(也就是取较大值),这样才没有白遍历。
int candy(vector<int>& ratings) {
vector<int> candys(ratings.size(),1);
//对于右边大于左边
for(int i=1;i<ratings.size();i++){
if(ratings[i]>ratings[i-1]) candys[i]=candys[i-1]+1;
}
//对于左边大于右边
for(int i=ratings.size()-2;i>=0;i--){
if(ratings[i]>ratings[i+1]) candys[i]=max(candys[i],candys[i+1]+1);
}
int sum=0;
for(int a:candys) sum+=a;
return sum;
}