Leedcode刷题 | Day28_贪心算法02

 一、学习任务

  • 122.买卖股票的最佳时机 II代码随想录
  • 55. 跳跃游戏
  • 45. 跳跃游戏 II
  • 1005.K次取反后最大化的数组和

二、具体题目

1.122买卖股票的最佳时机 II122. 买卖股票的最佳时机 II - 力扣(LeetCode)

给定一个数组,它的第  i 个元素是一支给定股票第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

  • 输入: [7,1,5,3,6,4]
  • 输出: 7
  • 解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4。随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。

假如第 0 天买入,第 3 天卖出,那么利润为:prices[3] - prices[0]。

相当于(prices[3] - prices[2]) + (prices[2] - prices[1]) + (prices[1] - prices[0])。

此时就是把利润分解为每天为单位的维度,而不是从 0 天到第 3 天整体去考虑!

重点:

  1. 第一天没有利润,至少要第二天才会有利润,所以利润的序列比股票序列少一天!
  2. 只需要收集每天的正利润就可以,收集正利润的区间,就是股票买卖的区间,只需要关注最终利润,不需要记录区间
  3. 那么只收集正利润就是贪心所贪的地方!
  4. 局部最优:收集每天的正利润,全局最优:求得最大利润

        检查过没有问题(个人更喜欢使用特殊例子来考虑边界条件和循环条件,这样不容易错)。

class Solution {
public:
    int maxProfit(vector& prices) {
        int result = 0;
        for (int i = 1; i < prices.size(); i++) {
            result += max(prices[i] - prices[i - 1], 0);
        }
        return result;
    }
};

2.55跳跃游戏55. 跳跃游戏 - 力扣(LeetCode)

给定一个非负整数数组,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个位置。

示例  1:

  • 输入: [2,3,1,1,4]
  • 输出: true
  • 解释: 我们可以先跳 1 步,从位置 0 到达 位置 1, 然后再从位置 1 跳 3 步到达最后一个位置。

示例  2:

  • 输入: [3,2,1,0,4]
  • 输出: false
  • 解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃长度是 0 , 所以你永远不可能到达最后一个位置。
  • 从起点开始,每次更新“可以跳到的最远位置(cover)”,然后在这个范围内每个能到达的位置都尝试“继续跳”,更新更远的 cover,直到能跳到终点或跳不到为止。
  • 即,cover 是一段“跳跃覆盖区间”,从里面的任意位置起跳都合法,我们不断扩大它,直到跳到终点。
class Solution {
public:
    bool canJump(vector& nums) {
        int cover = 0;
        if (nums.size() == 1) return true; // 只有一个元素,就是能达到
        for (int i = 0; i <= cover; i++) { // 注意这里是小于等于cover
            cover = max(i + nums[i], cover);
            if (cover >= nums.size() - 1) return true; // 说明可以覆盖到终点了
        }
        return false;
    }
};

3.45跳跃游戏 II   较难   没完全懂45. 跳跃游戏 II - 力扣(LeetCode)

给定一个非负整数数组,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

你的目标是使用最少的跳跃次数到达数组的最后一个位置。

示例:

  • 输入: [2,3,1,1,4]
  • 输出: 2
  • 解释: 跳到最后一个位置的最小跳跃数是 2。从下标为 0 跳到下标为 1 的位置,跳  1  步,然后跳  3  步到达数组的最后一个位置。

说明: 假设你总是可以到达数组的最后一个位置。

具体见注释,解释了两个最大范围的关系;

cur是计数过的最后一次跳,next是观望下次跳的可能范围,只有跳了,ans++了,才更新cur!!!!!

举例: nums = [2, 3, 1, 1, 4]

i = 0; curDistance = 0; nextDistance = 0 + 2 = 2; 

现在还没跳,当前活动范围就是起始点i = 0,满足i = cur,所以马上需要跳,ans++,这一跳的范围最远到i = 2, cur = 2, next没有大于结束点,循环继续。

i =1,开始在第一跳(ans = 1)的范围里面迭代,即从[0,cur = 2]范围内把每个可能的起跳点可到的最大距离计算一下,next = 1 + 3 = 4。

i = 2,next = 4,2 == 2,该跳了,ans = 2,cur = 4,next = 4 > 4,break。

class Solution {
public:
    int jump(vector& nums) {
        if (nums.size() == 1) return 0;

        // 当前覆盖最远距离下标(本次,也就是最后一次跳最远达到距离,表示下一次跳可以开始位置在这个范围内)
        int curDistance = 0;  
  
        // 记录走的最大步数
        int ans = 0;   

        // 下一步覆盖最远距离下标(这一次跳未被计数,所以每次判断这个长度以决定要不要继续跳,也就是增加ans)        
        int nextDistance = 0;   

        for (int i = 0; i < nums.size(); i++) {
            nextDistance = max(nums[i] + i, nextDistance);  // 更新下一步覆盖最远距离下标
            if (i == curDistance) {                         // 遇到当前覆盖最远距离下标
                ans++;                                  // 需要走下一步
                curDistance = nextDistance;             // 更新当前覆盖最远距离下标(相当于加油了)
                if (nextDistance >= nums.size() - 1) break;  // 当前覆盖最远距到达集合终点,不用做ans++操作了,直接结束
            }
        }
        return ans;
    }
};

4.1005K次取反后最大化的数组和1005. K 次取反后最大化的数组和 - 力扣(LeetCode)

给定一个整数数组 A,我们只能用以下方法修改该数组:我们选择某个索引 i 并将 A[i] 替换为 -A[i],然后总共重复这个过程 K 次。(我们可以多次选择同一个索引 i。)

以这种方式修改数组后,返回数组可能的最大和。

示例 1:

  • 输入:A = [4,2,3], K = 1
  • 输出:5
  • 解释:选择索引 (1) ,然后 A 变为 [4,-2,3]。
  1. 贪心的思路,局部最优:让绝对值大的负数变为正数,当前数值达到最大,整体最优:整个数组和达到最大。局部最优可以推出全局最优。
  2. 那么如果将负数都转变为正数了,K依然大于0,此时的问题是一个有序正整数序列,如何转变K次正负,让数组和达到最大。那么又是一个贪心:局部最优:只找数值最小的正整数进行反转,当前数值和可以达到最大,全局最优:整个数组和达到最大。
class Solution {
static bool cmp(int a, int b) {
    return abs(a) > abs(b);
}
public:
    int largestSumAfterKNegations(vector& 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;
    }
};

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