代码随想录算法训练营第三十四天| 1005.K次取反后最大化的数组和、134. 加油站、135. 分发糖果

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

题目链接:力扣

第一眼的想法是去分类讨论 
先将总的元素做sort排序
当元素全大于0的时候,则反转数组中最小的元素,k为负值的情况下,最小元素反转,k为偶数的情况下,最小元素不需处理(因为反转偶数次还是原来的元素)
当元素含小于0的时候,总体思路是先将负数的反转为正数,接着若负数的反转完了可是k还有剩余的反转次数,则 反转 【原来是负数现在反转为正数 和 第一个原来就是正数的数 中 较小的那一个数】,并反复反转该数,直到k的值用完。

class Solution {
public:
    int largestSumAfterKNegations(vector<int>& nums, int k) {

        sort(nums.begin(),nums.end());
        int res = 0;
        
        if(nums[0]>0)  //此时全员为正数
        {
            if(k%2)       //k为奇数 , k为偶数时不处理
             nums[0] = -nums[0];   //最小值取反
        }
        else if(nums[0]<0)   //nums含负数
        {
            int temp = k;
            int i = 0;
            while(temp)
            {
                cout<<temp<<endl;
                if(nums[i]<=0)
                 {
                     nums[i]=-nums[i]; 
                     i=i+1<nums.size()? i+1:i;
                     temp--;
                  }         //将负数转为正数
                else if(nums[i]>0 && nums[i-1]<=nums[i] && temp>0)
                 {nums[i-1] = -nums[i-1]; temp--;}
                else if(nums[i]>0 && nums[i-1] > nums[i] && temp>0)
                 {nums[i] = -nums[i]; temp--;}
            }
        }
       
       for(int i = 0; i < nums.size();i++)
           res+=nums[i];
       
       return res;
    }
};

卡哥给出的思路更加简洁:
贪心的思路,
局部最优:让绝对值大的负数变为正数,当前数值达到最大。
整体最优:整个数组和达到最大。
解题步骤:

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

 加油站 

 题目链接:力扣

这题!感觉思路很乱,后面看到了leetcode上有人的题解 感觉说的很好,这里引用一下:

有一个环形路上有n个站点; 每个站点都有一个好人或一个坏人; 好人会给你钱,坏人会收你一定的过路费,如果你带的钱不够付过路费,坏人会跳起来把你砍死; 问:从哪个站点出发,能绕一圈活着回到出发点?

首先考虑一种情况:如果全部好人给你 的钱加起来 小于 坏人收的过路费之和,那么总有一次你的钱不够付过路费,你的结局注定会被砍死。

假如你随机选一点 start 出发,那么你肯定会选一个有好人的站点开始,因为开始的时候你没有钱,遇到坏人只能被砍死;

现在你在start出发,走到了某个站点end,被end站点的坏人砍死了,说明你在 [start, end) 存的钱不够付 end点坏人的过路费,因为start站点是个好人,所以在 (start, end) 里任何一点出发,你存的钱会比现在还少,还是会被end站点的坏人砍死;

于是你重新读档,聪明的选择从 end+1点出发,继续你悲壮的征程; 终于有一天,你发现自己走到了尽头(下标是n-1)的站点而没有被砍死; 此时你犹豫了一下,那我继续往前走,身上的钱够不够你继续走到出发点Start?

当然可以,因为开始已经判断过,好人给你的钱数是大于等于坏人要的过路费的,你现在攒的钱完全可以应付 [0, start) 这一段坏人向你收的过路费。 这时候你的嘴角微微上扬,眼眶微微湿润,因为你已经知道这个世界的终极奥秘:Start就是这个问题的答案。

 首先如果总油量减去总消耗大于等于零那么一定可以跑完一圈,说明 各个站点的加油站 剩油量rest[i]相加一定是大于等于零的。

每个加油站的剩余量rest[i]为gas[i] - cost[i]。
i从0开始累加rest[i],和记为curSum,一旦curSum小于零,说明[0, i]区间都不能作为起始位置,因为这个区间选择任何一个位置作为起点,到i这里都会断油,那么起始位置从i+1算起,再从0计算curSum。

class Solution {
public:
    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {

         vector<int>a(gas.size());
         int sum =0;
         for(int i=0;i<gas.size();i++)
          {
              a[i] = gas[i] -cost[i];
              sum += a[i];
          } 

          if(sum < 0)    //总花销大于总收入,无论怎么走都走不出去
          return -1;
        
        int start = 0;
        sum = 0;

        for(int i=0; i<a.size();i++)
        {
            sum += a[i];
            if(sum < 0)
            {
                sum=0;
                start = i+1;
            }
        }

        return start;

    }
};

分发糖果

题目链接:力扣

 本题既要和左边的小孩进行比较,又要和右边的小孩进行比较,但如果两边一起考虑,会导致顾此失彼的情况。

需要考虑两边情况的题目,一定要先确定一边 再确定另一边。

我们先考虑 右边小孩比左边小孩得分高的情况。

        for(int i=1;i<ratings.size();i++)    //从前遍历,右边比左边大
        {
            if(ratings[i] > ratings[i-1])
            result[i] = result[i-1]+1;
            
        }

再考虑左孩子比右孩子得分高的情况,此时要从后向前遍历
由于前一步已经计算了i处右边孩子比左边孩子大是的糖果数(放入result[i]中),现在的左边孩子比右边孩子大时,糖果数为result[i+1]+1,将左边孩子比较情况和右边孩子比较情况 取一个最大值

        for(int i=ratings.size()-2; i>=0;i--)   //从后遍历,左边比右边大
        {
            if(ratings[i] > ratings[i+1])
            result[i] = max(result[i+1]+1,result[i]);
        }

最后将result累加,可以得到答案RES。

        for(int i=0;i<ratings.size();i++)
        RES += result[i];

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值