代码随想录day29|1005K次取反后最大化的数组和134加油站135分发糖果|01笔记

74 阅读1分钟
  • 1005K次取反后最大化的数组和

  • 代码随想录 (programmercarl.com)
  • 第一印象

  • 可以分情况讨论数组中负数的个数n与k的关系。
  • n<k即取反绝对值大的负数
  • n=k即取反全部负数
  • k-n为奇数则保留一个绝对值最小的负数
  • k-n为偶数则等同于全部取反
  • 讲解观后感

  • 利用按照绝对值的排列方式,帮助省略实现了k的讨论过程,很巧妙。
  • 解题代码

  •     func largestSumAfterKNegations(nums []int, K int) int {
        	sort.Slice(nums, func(i, j int) bool {
        		return math.Abs(float64(nums[i])) > math.Abs(float64(nums[j]))
        	})
          
        	for i := 0; i < len(nums); i++ {
        		if K > 0 && nums[i] < 0 {
        			nums[i] = -nums[i]
        			K--
        		}
        	}
        
        	if K%2 == 1 {
        		nums[len(nums)-1] = -nums[len(nums)-1]
        	}
        
        	result := 0
        	for i := 0; i < len(nums); i++ {
        		result += nums[i]
        	}
        	return result
        }
    
  • 134加油站

  • 代码随想录 (programmercarl.com)
  • 第一印象

  • 出发地肯定需要从一个有结余的节点开始,暴力的方法可以是遍历所有有结余的节点来检查。
  • 讲解观后感

    • 方法一
      全局最优的方法。直接求得所有节点的剩余值,并寻找可以解决最低值的节点。不一定算是贪心算法,但解法巧妙,理解起来也不难。
    • 方法二
      区间剩余和小于0那么起始点就向后加1的方法,采用了局部最优的方式,是点型的贪心思想。
  • 解题代码

  • 方法一
  •     func canCompleteCircuit(gas []int, cost []int) int {
        	curSum := 0
            min := math.MaxInt64
            var rest int
            for i:=0;i<len(gas);i++ {
                rest = gas[i] - cost[i]
                curSum += rest
        
                if  curSum < min {
                    min = curSum
                }
            }
            if curSum < 0 {
                return -1
            }
            if min>=0 {
                return 0
            }
            for j:=len(gas)-1;j>=0;j-- {
                rest = gas[j] - cost[j]
                min += rest
                if min>=0 {
                    return j
                }
            }
            return -1
        }
    
  • 方法二
  •     func canCompleteCircuit(gas []int, cost []int) int {
        	curSum := 0
        	totalSum := 0
        	start := 0
        	for i := 0; i < len(gas); i++ {
        		curSum += gas[i] - cost[i]
        		totalSum += gas[i] - cost[i]
        		if curSum < 0 {
        			start = i+1
        			curSum = 0
        		}
        	}
        	if totalSum < 0 {
        		return -1
        	}
        	return start
        }
    
  • 135分发糖果

  • 代码随想录 (programmercarl.com)
  • 第一印象

  • 想办法统计极值点出现的位置,并确定极值两头的高度。
  • 讲解观后感

  • 这在leetcode上是一道困难的题目,其难点就在于贪心的策略,如果在考虑局部的时候想两边兼顾,就会顾此失彼。
    本题卡尔采用了两次贪心的策略:
  • 一次是从左到右遍历,只比较右边孩子评分比左边大的情况。
  • 一次是从右到左遍历,只比较左边孩子评分比右边大的情况。
  • 所以就取candyVec[i + 1] + 1 candyVec[i] 最大的糖果数量,candyVec[i]只有取最大的才能既保持对左边candyVec[i - 1]的糖果多,也比右边candyVec[i + 1]的糖果多
  • 解题代码

  •     func candy(ratings []int) int {
            /**先确定一边,再确定另外一边
                1.先从左到右,当右边的大于左边的就加1
                2.再从右到左,当左边的大于右边的就再加1
            **/
            need := make([]int, len(ratings))
            sum := 0
            // 初始化(每个人至少一个糖果)
             for i := 0; i < len(ratings); i++ {
                 need[i] = 1
             }
             // 1.先从左到右,当右边的大于左边的就加1
            for i := 0; i < len(ratings) - 1; i++ {
                if ratings[i] < ratings[i+1] {
                    need[i+1] = need[i] + 1
                }
            }
            // 2.再从右到左,当左边的大于右边的就右边加1,但要花费糖果最少,所以需要做下判断
            for i := len(ratings)-1; i > 0; i-- {
                if ratings[i-1] > ratings[i] {
                    need[i-1] = findMax(need[i-1], need[i]+1)
                }
            }
            //计算总共糖果
            for i := 0; i < len(ratings); i++ {
                sum += need[i]
            }
            return sum
        }
        func findMax(num1 int, num2 int) int {
            if num1 > num2 {
                return num1
            }
            return num2
        }