随想录一刷Day34——贪心算法

Day34_贪心算法

9. K 次取反后最大化的数组和

1005. K 次取反后最大化的数组和
思路:

对数组降序排列,遍历数组(从大到小),遇到负数优先将绝对值较大的负数转化为正数,这是局部最优的。如果 k 不够将所有负数都变为正数,则将绝对值较大的部分转为正数即可;如果将所有负数都转变为正数时,k 还有剩余,不断反转最小的数即可,最终若 k 剩余奇数,则最小的数为负,偶数为正。

class Solution {
public:
    static bool cmp(const int a, const int b) {
        return abs(a) > abs(b);
    }

    int largestSumAfterKNegations(vector<int>& nums, int k) {
        int nums_size = nums.size();
        int sum = 0;
        sort(nums.begin(), nums.end(), cmp);
        for (int i = 0; i < nums_size; ++i) {
            if (nums[i] < 0 && k > 0) {
                k--;
                nums[i] = -nums[i];
            }
            sum += nums[i];
        }
        if (k & 1) sum -= 2 * nums[nums_size - 1]; // 如果还需要反转奇数次
        return sum;
    }
};

10. 加油站

134. 加油站
思路一:

暴力解法:
模拟整个过程,最坏情况每个点都走到最后失败,复杂度 O ( n 2 ) O(n^2) O(n2)
有一个样例过不了

class Solution {
public:
    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        int gas_size = cost.size();

        for (int i = 0; i < gas_size; ++i) { // 枚举起点位置
            int rest_gas = gas[i] - cost[i];
            int cur_index = (i + 1) % gas_size; // 可能下一个位置已经回到数组开头了
            while (rest_gas > 0 && cur_index != i) { // 必须是大于,如果等于0,无法下一次起步
                rest_gas += gas[cur_index] - cost[cur_index];
                cur_index = (cur_index + 1) % gas_size;
            }
            if (rest_gas >= 0 && cur_index == i) return i; // 最后一次的可把油用完
        }
        return -1;
    }
};

思路二:
直接参考卡哥写法

直接从全局进行贪心选择,情况如下:

情况一:如果gas的总和小于cost总和,那么无论从哪里出发,一定是跑不了一圈的

情况二:rest[i] = gas[i]-cost[i]为一天剩下的油,i从0开始计算累加到最后一站,如果累加没有出现负数,说明从0出发,油就没有断过,那么0就是起点。

情况三:如果累加的最小值是负数,汽车就要从非0节点出发,从后向前,看哪个节点能这个负数填平,能把这个负数填平的节点就是出发节点。

class Solution {
public:
    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        int gas_size = cost.size();
        int cur_rest = 0;
        int min_rest = 0;
        for (int i = 0; i < gas_size; ++i) {
            cur_rest += gas[i] - cost[i];
            if (cur_rest < min_rest) min_rest = cur_rest;
        }
        if (cur_rest < 0) return -1;
        if (min_rest >= 0) return 0;

        for (int i = gas_size - 1; i >= 0; i--) { // 枚举起点位置
            min_rest += gas[i] - cost[i];
            if (min_rest >= 0) return i;
        }
        return -1;
    }
};

思路三:

  1. 总油量小于总消耗量的话,一定不能到达。
  2. 如果总油量不小于总消耗量的话,一定能到达。从头开始遍历所有的加油站,如果出现剩余油量为负数的情况,一定在后面存在剩余油量过剩的情况,可以在循环一圈回来的时候补全前面的亏空。从某个点出发一直到到达最后一个点都一直处于油有剩余的情况,则该点就是出发点。
class Solution {
public:
    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        int gas_size = cost.size();
        int cur_rest = 0;
        int tot_rest = 0;
        int start_pos = 0;
        for (int i = 0; i < gas_size; ++i) {
            cur_rest += gas[i] - cost[i];
            tot_rest += gas[i] - cost[i];
            if (cur_rest < 0) {
                cur_rest = 0;
                start_pos = i + 1;
            }
        }
        if (tot_rest < 0) return -1;
        return start_pos;
    }
};

12. 分发糖果

135. 分发糖果

一开始想着两边都考虑,想不明白了,看了题解直呼妙不可言
分别从左到右,和从右到左考虑两次要简单很多。
从左到右考虑如果右边的评分高,给右边比左边多一个糖
从右到左,如果左边比右边评分高,给左边比右边多一个糖,但是还要考虑到左边的孩子要比他左边的孩子多一个糖(这在前一步考虑过了),所以综合考虑,左边孩子的糖数应该是其当前糖的数量与比右边孩子多一个糖的数量之中的最大值。

class Solution {
public:
    int candy(vector<int>& ratings) {
        int child_size = ratings.size();
        vector<int> candy_list (child_size, 1); // 开始每个孩子至少有一个糖
        int result = 0;
        for (int i = 1; i < child_size; ++i) { // 从左向右
            if (ratings[i] > ratings[i - 1]) candy_list[i] = candy_list[i - 1] + 1;
        }
        for (int i = child_size - 1; i > 0; --i) {
            if (ratings[i - 1] > ratings[i]) candy_list[i - 1] = max(candy_list[i - 1], candy_list[i] + 1);
        }
        for (int i = 0; i < child_size; ++i) result += candy_list[i];

        return result;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第二十二天的算法训练营主要涵盖了Leetcode题目中的三道题目,分别是Leetcode 28 "Find the Index of the First Occurrence in a String",Leetcode 977 "有序数组的平方",和Leetcode 209 "长度最小的子数组"。 首先是Leetcode 28题,题目要求在给定的字符串中找到第一个出现的字符的索引。思路是使用双指针来遍历字符串,一个指向字符串的开头,另一个指向字符串的结尾。通过比较两个指针所指向的字符是否相等来判断是否找到了第一个出现的字符。具体实现的代码如下: ```python def findIndex(self, s: str) -> int: left = 0 right = len(s) - 1 while left <= right: if s[left == s[right]: return left left += 1 right -= 1 return -1 ``` 接下来是Leetcode 977题,题目要求对给定的有序数组中的元素进行平方,并按照非递减的顺序返回结果。这里由于数组已经是有序的,所以可以使用双指针的方法来解决问题。一个指针指向数组的开头,另一个指针指向数组的末尾。通过比较两个指针所指向的元素的绝对值的大小来确定哪个元素的平方应该放在结果数组的末尾。具体实现的代码如下: ```python def sortedSquares(self, nums: List[int]) -> List[int]: left = 0 right = len(nums) - 1 ans = [] while left <= right: if abs(nums[left]) >= abs(nums[right]): ans.append(nums[left ** 2) left += 1 else: ans.append(nums[right ** 2) right -= 1 return ans[::-1] ``` 最后是Leetcode 209题,题目要求在给定的数组中找到长度最小的子数组,

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值