贪心算法的“左最优“与“右最优“

1 答疑

1.1 什么是贪心算法的"左最优"与"右最优"

"左最优"和"右最优"是贪心算法中的两种策略:

  1. 左最优 (Leftmost Greedy): 在每一步选择中,总是选择最左边(最早出现的)可行的选项。

  2. 右最优 (Rightmost Greedy): 在每一步选择中,总是选择最右边(最晚出现的)可行的选项。

这两种策略是贪心算法中根据具体问题选择的不同方向。

1.2 这两种最优问题,分别用什么方法解决?

一般左最优可以采取边遍历边找出"当前的最值", 右最优不能向左最优一样通过左到右的遍历稍带最值,所以一般需要预处理从右到左遍历并且将得到的最值存放到数组中。

2 “左最优” 类题目

2.1 字节笔试题

小明在玩一场通关游戏,初始血量为1,关卡有怪兽或者有血包(正数就是血包可回血数,负数说明是怪兽的伤害值),当捡到血包时会加血量,碰到怪兽时会掉血,现在指定初始血量为x,关卡是一个数组,小明必须按照数组的顺序玩游戏,当碰到一个怪兽时,他可以选择将这个怪兽扔到数组末尾,小明可以无限次地将怪兽移到数组末尾,问小明最少移动几次就能存活,如果无论怎么移动都不能存活则返回-1, 假设关卡是这样的[-200,-300,400],则返回-1,假如是这样的[200,100,-250,-60,-70,100],则返回1,只需要把-250挪到尾部,

思路:当发现自己血量不足时,就从当前已经遍历过的所有关卡中,选择耗费血量最多的那个关卡并且放到最后一关,如果即使这样挪开了耗血量最大的一关自身血量还是为负,则直接返回-1,说明无法通关

———————————————— 版权声明:本文为CSDN博主「xxx_520s」的原创文章,遵循CC 4.0
BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yxg520s/article/details/131989933

2.2 leetcode 1642. 可以到达的最远建筑

这样的贪心思路,871题最低加油次数和630课程表III也有体现。

两种贪心策略均可:
优先使用梯子,梯子不够时选取差值最小的出堆改用砖头。(小根堆)
优先使用砖头,砖头不够时选取消耗最大的出堆改用梯子。(大根堆)
解法一的理论复杂度应该是最低的。

class Solution {
    public int furthestBuilding(int[] heights, int bricks, int ladders) {
        int n = heights.length, sum = 0;
        Queue<Integer> queue = new PriorityQueue<>();
        for(int i = 1; i < heights.length; i++) {
            int diff = heights[i] - heights[i - 1];
            if(diff > 0) {
                queue.offer(diff);
                if(queue.size() > ladders) {
                    sum += queue.poll();
                }
                if(sum > bricks)
                    return i - 1;
            }
        }
        return n - 1;
    }
}

作者:onion12138
链接:https://leetcode.cn/problems/furthest-building-you-can-reach/description/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

2.3 股票问题:121. 买卖股票的最佳时机(虽然使用堆的方法不是最优解,但是方便和其他题目对比得出最优解)

	// 使用堆维护最小值
   	public int maxProfit(int[] prices) {
        int n=prices.length;
        //表示到第i天时,股票的历史最低点价格
        PriorityQueue<Integer>pq=new PriorityQueue<>((o1,o2)->(o1-o2));
        
        int max=0;
        for(int i=0;i<n;i++){
            pq.add(prices[i]);
            max=Math.max(max,prices[i]-pq.peek());
        }
        return max;
    }

3 “右最优” 类题目

3.1 LC670. 最大交换

搞懂两个问题,便可彻底理解本题的**(右最优)贪心核心**。

选择哪个数作为候选数与前面的数交换?——将最靠后的数定为候选数,若它之前出现了更大的数,则更新候选数为该数。
选择哪个数与候选数交换?——只有当候选数之前存在更小的数时,才需要交换这两数。若更靠前的位置出现小于候选数的数,则将它与候选数交换。

class Solution {
    public int maximumSwap(int num) {
        char[] cs = String.valueOf(num).toCharArray();
        int n = cs.length, max = n - 1;
        int[] maxIdx = new int[n]; // 记录每个数从当前位置往右的最大值索引
        for (int i = n - 1; i >= 0; i--) {
            if (cs[i] > cs[max]) max = i;
            maxIdx[i] = max;
        }
        for (int i = 0; i < n; i++) { // 从前往后找到第一个最大值不是自己本身的数
            if (cs[i] != cs[maxIdx[i]]) {
                char tmp = cs[i];
                cs[i] = cs[maxIdx[i]];
                cs[maxIdx[i]] = tmp;
                return Integer.parseInt(new String(cs));
            }
        }
        return num;
    }
}

4 同时涉及到左最优和右最优的题目

4.1 LC42 接雨水(需要两边同时进行预处理)

class Solution {
    public int trap(int[] height) {
        int n = height.length;
        if (n == 0) {
            return 0;
        }

        int[] leftMax = new int[n];
        leftMax[0] = height[0];
        for (int i = 1; i < n; ++i) {
            leftMax[i] = Math.max(leftMax[i - 1], height[i]);
        }

        int[] rightMax = new int[n];
        rightMax[n - 1] = height[n - 1];
        for (int i = n - 2; i >= 0; --i) {
            rightMax[i] = Math.max(rightMax[i + 1], height[i]);
        }

        int ans = 0;
        for (int i = 0; i < n; ++i) {
            ans += Math.min(leftMax[i], rightMax[i]) - height[i];
        }
        return ans;
    }
}

作者:力扣官方题解
链接:https://leetcode.cn/problems/trapping-rain-water/solutions/692342/jie-yu-shui-by-leetcode-solution-tuvc/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

你可能感兴趣的:(数据结构,贪心算法,java)