算法学习记录~2023.5.17~贪心Day2~122.买卖股票的最佳时机II & 55. 跳跃游戏 & 45.跳跃游戏II & 1005. K 次取反后最大化的数组和


122.买卖股票的最佳时机II

题目链接

力扣题目链接

思路1:贪心

将总利润细分为每两天之间那一整天的利润,只要是正的那就可以加入,否则就不操作。

局部最优:收集每天的正利润
全局最优:求得最大利润

因此根据价格序列构建一个利润序列(长度会比价格序列短一个,因为至少得有两天才有买卖交易可能性),有正的那就都是局部最优,然后累加即可

代码

//自己写的
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int sum = 0;
        vector<int> difference(prices.size() - 1);     //差值
        for (int i = 0; i < difference.size(); i++){
            difference[i] = prices[i + 1] - prices[i];
        }
        for (int i = 0; i < difference.size(); i++){
            if (difference[i] > 0){
                sum += difference[i];
            }
        }
        return sum;
    }
};

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

思路2:动态规划

完成下一章节动态规划后再补

总结


55. 跳跃游戏

题目链接

力扣题目链接

思路

问题可以转化为跳跃覆盖范围究竟可不可以覆盖到终点,因为数组对应的值是最大跳跃长度,因此小于它的所有地方都一定可以到达。

从第一个点的值取得第一个范围,在该范围下的每一个点,根据取值都能获得新的范围,不断更新最大值。

通过不断更新范围和遍历该范围下所有点能到的最远距离,就能求出来从第一个点出发能到达的最远范围。

如果该范围大于数组长度,则说明一定能覆盖到。

局部最优解:每次取最大跳跃步数(取最大覆盖范围)
整体最优解:最后得到整体最大覆盖范围,看是否能到终点。

代码

class Solution {
public:
    bool canJump(vector<int>& nums) {
        int range = 0;                          //能到达的最大范围
        if (nums.size() == 1)                   //一个数的话一定能达到
            return true;
        for (int i = 0; i <= range; i++){       //第一注意是range,第二注意要 <=
            range = max(i + nums[i], range);
            if (range >= nums.size() - 1)       //range超过nums的大小则一定能覆盖到
                return true;
        }
        return false;
    }
};

总结


45.跳跃游戏II

题目链接

力扣题目链接

思路

同样是看覆盖范围。

想以最小步数到达终点,那就一步步进行计算,确保每一步都尽可能走远,一旦覆盖了终点,那就是最小步数。

使用一个 curRange 和 nextRange,分别记录当前最远范围和加一步的话的最远距离。从第一个点开始遍历,在curRange范围内的每个数都进行nextRange的更新,只取最大值。只有nextRange能够到达最后位置时才退出获得答案,否则就继续往后一步重复上述过程。

代码

class Solution {
public:
    int jump(vector<int>& nums) {
        if (nums.size() == 1)
            return 0;
        int curRange = 0 ;            //初始最大距离
        int nextRange = 0 + nums[0];            //初始下一步最大距离
        int count = 0;                //初始步数
        for (int i = 0; i < nums.size(); i++){
            nextRange = max(nextRange, i + nums[i]);    //更新当前范围下每一步能走的最大距离

            //由于使用了nextRange判断是否能到最后一步,因此只需不断更新当前能走范围内的最大距离
            //当走到当前范围尽头时如果还没到达最远,则需要加一步,更新当前最大距离为nextRange
            //如果nextRange能到达最后位置则已经找到答案
            //否则继续遍历更新下一步范围内能到的最远位置,重复上述步骤
            if (i == curRange){                    //走到了当前范围内能走的最远位置
                if (curRange < nums.size() - 1){   //到了当前最远距离但还是没走到最后,说明还需要后续步数
                    count ++;                       //再走一步
                    curRange = nextRange;           //更新当前最大范围
                    if (nextRange >= nums.size() - 1)   //下一步能到最后位置则直接退出
                        break;
                }
            }
        }
        return count;
    }
};

总结

一开始考虑的是找出所有能走到最后一步的路径,之后再找出步数最少的一个。但后来发现这样非常复杂,而且暂时没想出来具体解决方案。
看了题解后才想到尽量使用贪心思路,改变思路考虑,一步一步地算,每一步考虑好最优解,最终就是全局最优解


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

题目链接

力扣题目链接

思路1:比较差的自己想的懒方法

取最大值的话,那就每次修改都只修改最小的值

因此每次都先排序一下,然后把最小值取反,直到用完k

思路比较好想,但每次都排序一下显然会成本更高

代码

class Solution {
public:
    int largestSumAfterKNegations(vector<int>& nums, int k) {
        //取最大值的话,那就每次修改都只修改最小的值,因此每次都排序一下然后把最小值取反
        while (k != 0){
            k--;                                     //统计次数
            sort(nums.begin(), nums.end()); //排序
            nums[0] = -nums[0];                      //最小值取反
        }
        int sum = 0;                                //统计数组和
        for (int i = 0; i < nums.size(); i++){
            sum += nums[i];
        }
        return sum;
    }
};

思路2:分情况进行贪心考虑

主要有两种情况:

  1. 如果有负数 —> 将绝对值更大的负数取反则总和一定更大
  2. 如果负数用完 / 全都是正数 —> 找数值最小的正整数进行取反则总和一定更大

因此具体的解决思路是:

  1. 将数组按照绝对值大小大到小排序,必须是按照绝对值大小
  2. 从前向后遍历,遇到负数将其变为正数,同时k–
  3. 如果遍历到头k还大于0,那就反复取反数值最小的元素直到将k用完
  4. 求数组总和

代码

class Solution {
public:
    static bool cmp(int a, int b){      //设置排序规则为绝对值从大到小
        return abs(a) > abs(b);
    }

    int largestSumAfterKNegations(vector<int>& nums, int k) {
        sort(nums.begin(), nums.end(), cmp);    //数组排序
        int sum = 0;        //用于求和
        for (int i = 0; i < nums.size(); i++){      //先将所有负数或0按绝对值从大到小进行取反
            if (nums[i] <= 0 && k > 0){
                nums[i] = -nums[i];
                k --;
            }
            if (k == 0)
                break;
        }
        //此时如果k还没用完则数组已经全变成了正数数组
        while (k > 0){                      //如果k还没用完就不断取反数值最小的一个
            nums[nums.size() - 1] = -nums[nums.size() - 1];
            k --;
        }
        for (int i = 0; i < nums.size(); i++)   //数组求和
            sum += nums[i];
        return sum;
    }
};

总结

按照贪心思想考虑事件划分为小事件后的所有情况,然后再找到解决方案,一方面能解决问题,一方面能尽可能提高效率。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

山药泥拌饭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值