Day34贪心_k次取反数组和最大_加油站_分发糖果

文章目录

      • 1005.K次取反后最大化的数组和
      • [134 加油站](https://programmercarl.com/0134.%E5%8A%A0%E6%B2%B9%E7%AB%99.html#%E7%AE%97%E6%B3%95%E5%85%AC%E5%BC%80%E8%AF%BE)
      • [135 分发糖果](https://programmercarl.com/0135.%E5%88%86%E5%8F%91%E7%B3%96%E6%9E%9C.html#%E6%80%9D%E8%B7%AF)

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

  1. 数组有正有负,如果按照正负的顺序排列,经过小于等于k次的取反,负数变为正数,会改变数组原来的顺序,如果再排序,需要快排(nlogn)时间复杂度。
  2. 所以希望用 堆 实现,每次都可以取到数组的最小值,让它取反。
  3. 如果用 绝对值数组 的有序数组,则 绝对值最小 就是数组的第一个元素。从后到前将负数取反可以保证负数取反是按照 从小到大 的顺序。负数取完反以后就对 第一个元素 一直取反,直到k=0。

第一步:将数组按照绝对值大小 从小到大 排序,注意要按照绝对值的大小
第二步:从 后向前 遍历,遇到负数将其变为正数,同时K–
第三步:如果K还大于0,那么反复转变数值最小的元素,将K用完
第四步:求和

绝对值从小到大

static bool cmp(int a, int b) {
    return abs(a) < abs(b);
    }
public:
    int largestSumAfterKNegations(vector<int>& nums, int k) {
        sort(nums.begin(), nums.end(), cmp);
        int sum = 0;
        for(int i = nums.size()-1; i >= 0; i--){
            if(nums[i] < 0 && k){
                nums[i] *= -1;
                k--;
            }
        }
        if( k%2 != 0) nums[0] *= -1;
        for(int i : nums) sum += i;
        return sum;
    }

真值从小到大

int largestSumAfterKNegations(vector<int>& nums, int k) {
        //最后是最大值,最次更换最小值
        //用堆实现,每次取最小值
        sort(nums.begin(), nums.end());
        int sum  = 0;
        for(int i = 0; i < nums.size(); i++){
            if (i == 0 && nums[0] >= 0){
                if (k % 2 != 0) {
                    nums[0] = -nums[0];
                }
                k = 0;
            }
            else if(nums[i] < 0 && k > 0){
                nums[i] = -nums[i];
                k--;
                if(i+1 < nums.size() && nums[i+1] >= 0 && nums[i] < nums[i+1]){
                    if(k % 2 != 0) {
                        nums[i] = -nums[i];
                    }
                    k = 0;
                }
            }
            else if(nums[i] >= 0 && k > 0){
                if(k % 2 != 0) {
                    nums[i] = -nums[i];
                }
                k = 0;
            }
            if( i == nums.size() -1 && k != 0){
                if(k % 2 != 0) {
                    nums[i] = -nums[i];
                }
            }
            sum += nums[i];
        }
        return sum;
    }

134 加油站

  1. 为什么curSum < 0 的时候直接说起点从 j+1 开始:递推,前面一直大于0累加,到了小于0,说明从前面的任意一个位置开始的累加都会小于原来的值,到这个点 curSum 都小于0。
  2. 如何体现了 局部和全局 的思想:全局最优是所有的 gas[i] - cost[i] >= 0,则i就是出发的起点。局部最优就是curSum = gas[i] - cost[i],当curSum < 0,则从小于0的下一个点开始才有可能是起点。

135 分发糖果

  1. 从前到后分发糖果的时候,遇到这样的情况:前面的小于后面的,后面的糖果在前面的基础上+1就行。如果是前面 大于 后面,前面 = 后面+1, 后面 大于 后后面,后面 = 后后面+1,后面的改变了,前面的也需要改变才能符合规则。所以从前到后只能保证 前小于后 的分发结果是对的。
  2. 进而用逆向的思路,两个不等的情况只有:前 小于 后; 前 大于 后。从前到后会使 前小于后 的顺序正确,则从后到前会使 后小于前 即 前大于后 的顺序正确。
  3. 时间复杂度:O(n),空间复杂度:O(n)。
class Solution {
public:
    int candy(vector<int>& ratings) {
        //虽然是相邻两个相比,但是后面的改变会对前面的造成影响
        vector<int> candies(ratings.size(), 1);
        int sum = 0;
        //保证顺序肯定是对的,前 小于 后则后分到的是正确的,但是后小于前不一定对
        for (int i = 0; i < ratings.size() - 1;i++){
            if(ratings[i] < ratings[i+1]){
                candies[i+1] = candies[i] + 1;
            }
        }
        // 后 小于 前
        for (int i = ratings.size() - 1; i > 0; i--){
            if(ratings[i-1] > ratings[i] && candies[i-1] <= candies[i]){
                candies[i-1] = candies[i]+1;
            }
            sum += candies[i];
        }
        sum += candies[0];

        return sum;
    }
};

你可能感兴趣的:(算法,数据结构,leetcode,力扣,C++)