贪心算法

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

题意:给你一个整数数组 nums 和一个整数 k ,按以下方法修改该数组:选择某个下标 i 并将 nums[i] 替换为 -nums[i] 。

重复这个过程恰好 k 次。可以多次选择同一个下标 i 。以这种方式修改数组后,返回数组 可能的最大和 。

示例:

算法练习-day27_贪心算法

思路:本题的思路主要在两次排序,第一次排序,是为了消耗k的次数,将局部最大负数变为正数;第二次排序,是为了消耗掉多余的k,将最小影响全局元素进行改变,直到所有k消耗完,主打的就是性价比极高的操作,最终得出数组的最大值

C++代码:

    int largestSumAfterKNegations(vector<int>& nums, int k) {
        sort(nums.begin(),nums.end());
        for(int i=0;i<nums.size();i++)//先将可以变成正数的负数变成正数
        {
            if(nums[i]<0&&k!=0)
            {
                nums[i]*=-1;
                k--;
            }
        }
        sort(nums.begin(),nums.end());//再次给出排序
        while(k--)//如果还没到达改变次数,就改变影响最小的元素
        {
            nums[0]*=-1;
        }
        int count=0;
        for(auto e:nums)//累加即可
        {
            count+=e;
        }
        return count;
    }

134. 加油站

题意:在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。

你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。

给定两个整数数组 gas 和 cost ,如果你可以按顺序绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1 。如果存在解,则 保证 它是 唯一 的。

示例:

算法练习-day27_贪心算法_02

思路:我们给出三个变量:全局剩余油量total,局部剩余油量parttotal和起始位置begin,大体分为两种情况:total小于0时,说明无论如何都跑不了一圈;total大于等于0时,说明可以跑完一圈,此时需要从0开始,进行剩余油量的累加,当parthtotal小于0时,说明从起始位置到i的这部分是一定不可能走完的,因此起始位置变为i+1,parthtotal重置,重新开始累加

C++代码:

    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        int total=0,partTotal=0,begin=0;//表示:一圈的油量,局部油量,起始位置
        for(int i=0;i<gas.size();i++)
        {
            total+=gas[i]-cost[i];
            partTotal+=gas[i]-cost[i];
            if(partTotal<0)//当局部油量出现小于0的情况,就说明从begin位置出发不正确,此时起始位置就可以变为i+1,因为之前的已经测试过了,不能到达i
            {
                begin=i+1;
                partTotal=0;
            }
        }
        if(total<0)//还是需要检验以下总体剩余油量必须不为负数
        {
            return -1;
        }
        return begin;
    }

135. 分发糖果

题意:n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。你需要按照以下要求,给这些孩子分发糖果:

  1. 每个孩子至少分配到 1 个糖果。
  2. 相邻两个孩子评分更高的孩子会获得更多的糖果。

请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。

示例:

算法练习-day27_贪心算法_03

思路:本题我们,先创建一个数组用于存储孩子可以获得的最少糖果数,初始化为1,然后两次遍历完成:正序遍历和倒序遍历

  1. 正序遍历:先对正序的相邻糖果进行累加
  2. 倒序遍历:对倒序的相邻糖果进行累加,此时必须满足两个条件才能进行糖果的调整:1.前一个小孩的评分必须大于后一个小孩的评分 2.前一个小孩得到的糖果必须小于后一个小孩得到的糖果

只有满足这两个条件,倒序遍历时,孩子的糖果才能增加

C++代码:

    int candy(vector<int>& ratings) {
        vector<int> dp(ratings.size(), 1);
        for (int i = 1; i<ratings.size(); i++)//先正序遍历,将正序中遇到的评分高的孩子糖果数累加1
        {
            if (ratings[i]>ratings[i - 1])
            {
                dp[i] += dp[i - 1];
            }
        }
        for (int i = ratings.size() - 1; i>0; i--)//倒序遍历,此时改变糖果数需要满足两个条件
        {
            if (ratings[i - 1]>ratings[i]&&dp[i-1]<=dp[i])
            {
                dp[i - 1] += dp[i]-dp[i-1]+1;//添加的糖果数必须最小达标
            }
        }
        int count = 0;//最小糖果总数
        for (auto e : dp)
        {
            count += e;
        }
        return count;
    }