栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

LeetCode 题解随笔:贪心算法

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

LeetCode 题解随笔:贪心算法

目录

零、前言

一、简单问题

455. 分发饼干

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

860. 柠檬水找零

二、序列问题

376. 摆动序列[*]

 738. 单调递增的数字

三、股票问题

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

714. 买卖股票的最佳时机含手续费[*]

四、两个维度权衡问题

135. 分发糖果[*]


零、前言

贪心算法使用原则:手动模拟一下,若可以通过局部最优推出整体最优,而且想不到反例,那么就试一试贪心

贪心算法一般步骤:

  • 将问题分解为若干个子问题
  • 找出适合的贪心策略
  • 求解每一个子问题的最优解
  • 将局部最优解堆叠成全局最优解

一、简单问题

455. 分发饼干
int findContentChildren(vector& g, vector& s) {
        int i = 0;
        sort(g.begin(), g.end());
        sort(s.begin(), s.end());
        for (int j = 0; j < s.size(); j++) {
            if (i <= g.size() && g[i] <= s[j]) {
                i++;
            }
        }
        return i;
    }

小饼干先满足胃口小的孩子/大饼干先满足胃口大的孩子均可。

1005. K 次取反后最大化的数组和
int largestSumAfterKNegations(vector& nums, int k) {
        // 尽可能把所有负数变成正数
        sort(nums.begin(), nums.end());
        for (int i = 0; i < nums.size(); i++) {
            if (nums[i] < 0) {
                if (k > 0) {
                    nums[i] = -nums[i];
                    k--;
                }
            }
        }
        // 此时若k>0,数组一定全为正数,只需处理k为奇数的情况
        sort(nums.begin(), nums.end());
        if (k > 0 && k % 2 != 0) {
            nums[0] = -nums[0];
        }
        return accumulate(nums.begin(), nums.end(), 0);
    }

本题采用贪心思想,尽可能把所有负数变为正数。若执行该操作后k仍然大于0,只需处理k为奇数的情况,取反最小的元素即可。

860. 柠檬水找零
bool lemonadeChange(vector& bills) {
        int fiveCnt = 0;
        int tenCnt = 0;
        for (auto i : bills) {
            if (i == 5)  fiveCnt++;
            else if (i == 10) {
                if (fiveCnt < 1)   return false;
                fiveCnt--;
                tenCnt++;
            }
            else {
                // 优先给出10元
                if (fiveCnt >= 1 && tenCnt >= 1) {
                    fiveCnt--;
                    tenCnt--;
                }
                else if (fiveCnt >= 3) {
                    fiveCnt -= 3;
                }
                else   return false;
            }
        }
        return true;
    }

本题较简单,分别处理三种情况即可。其中20元找零时用到了贪心思想。


二、序列问题

376. 摆动序列[*]
int wiggleMaxLength(vector& nums) {
        // 仅有一个元素或者含两个不等元素的序列也视作摆动序列
        if (nums.size() <= 1)    return nums.size();
        int deltaOld = 0;
        // 统计数组的峰值数量(默认开始有一个峰值)
        int res = 1;
        for (int i = 1; i < nums.size(); i++) {
            int deltaNew = nums[i] - nums[i - 1];
            if ((deltaOld <= 0 && deltaNew > 0) || (deltaOld >= 0 && deltaNew < 0)) {
                res++;
                deltaOld = deltaNew;
            }
        }
        return res;
    }

 如上图所示(来源:代码随想录),本题只需要统计数组中的峰值数量即可,即:前一个差值≤0,后一个差值>0或前一个差值≥0,后一个差值<0。

如下图所示(来源:代码随想录),注意其中的“=”符号,这样的情况也应被合法收入结果。

 738. 单调递增的数字
int monotoneIncreasingDigits(int n) {
        if (n < 10)  return n;
        string strNum = to_string(n);
        int index = strNum.size();
        for (int i = strNum.size() - 1; i > 0; i--) {
            if (strNum[i - 1] > strNum[i]) {
                index = i;
                strNum[i - 1]--;
            }
        }
        for (int j = index; j < strNum.size(); j++) {
            strNum[j] = '9';
        }
        return stoi(strNum);
    }

本题有两个技巧:

  • 邻位数字比较大小,可以转化为字符串进行操作,而不需要额外开辟数组储存每位数字;
  • 从右向左进行操作。贪心体现在结果字符串strNum直接等于原数字(尽可能大),从右向左操作,若前一位减一后小于前前一位,也可以继续迭代修改数字。

本题若采用从左向右循环的方式代码如下:

if (n < 10)    return n;
        // 贪心,尽可能取对应数位上的最大值
        string strNum = to_string(n);
        for (int i = 1; i < strNum.size(); i++) {
            // 若不满足,前一位减一,后续位置全部置9
            if (strNum[i - 1] > strNum[i]) {
                int index = i;
                for (int j = index + 1; j < strNum.size(); j++) {
                    strNum[j] = '9';
                }
                while (index - 1 >= 0 && strNum[index - 1] > strNum[index]) {
                    strNum[index] = '9';
                    strNum[index - 1]--;
                    index--;
                }
                break;
            }
        }
        return stoi(strNum);

出现不满足条件的元素,就可以直接退出循环,减少循环次数。


三、股票问题

122. 买卖股票的最佳时机 II
int maxProfit(vector& prices) {
        int res = 0;
        for (int i = 0; i < prices.size() - 1; i++) {
            int delta = prices[i + 1] - prices[i];
            if (delta > 0)     res += delta;
        }
        return res;
    }

贪心思想,收集每天的正利润即可。

714. 买卖股票的最佳时机含手续费[*]
int maxProfit(vector& prices, int fee) {
        int res = 0;
        // 记录最低价格
        int min = prices[0];
        for (int i = 1; i < prices.size(); i++) {
            // 1.低值时买入
            if (prices[i] < min)    min = prices[i];
            // 2.没有盈利时不操作
            if (prices[i] - min <= fee)    continue;
            // 3.盈利时计算利润(可能计算多次,但会更新最大利润)
            if (prices[i] - min > fee) {
                res += prices[i] - min - fee;
                // 若后续继续盈利,再进行该循环时会扣除上次操作的手续费(res += prices[i+1] - prices[i])
                // 若后续不盈利,不影响最小值被更新[看成新的一天,新的序列]
                min = prices[i] - fee;
            }
        }
        return res;
    }

本题对于股票的操作存在三种情况:

  • 情况一:前一天是收获利润区间里的最后一天(相当于真正的卖出了),今天要重新记录最低价格;
  • 情况二:没有盈利,但也不是最低价格。不作操作,保持原有状态;
  • 情况三:减去手续费后仍是递增数列,后面要继续收获利润。

情况三代码较难理解,可以把记录了累加结果后的数列看成新的数列,但第一个元素减去了手续费。后序序列的操作不受前面的影响:若第一个元素仍为min,继续循环即可,且得到的res差值不会累计手续费;若第一个元素非min,min会被更新。


四、两个维度权衡问题

135. 分发糖果[*]
int candy(vector& ratings) {
        vector res(ratings.size(), 1);
        // 处理右边比左边大的情况
        for (int i = 1; i < ratings.size(); i++) {
            if (ratings[i] > ratings[i - 1])   res[i] = res[i - 1] + 1;
        }
        // 处理左边比右边大的情况
        for (int j = ratings.size() - 2; j >= 0; j--) {
            if (ratings[j] > ratings[j + 1]) {
                res[j] = max(res[j + 1] + 1, res[j]);
            }
        }
        return accumulate(res.begin(), res.end(), 0);
    }

本题采用两次贪心的策略:

  • 先从左到右遍历,处理右边孩子评分比左边大的情况;
  • 再从右到左遍历,处理左边孩子评分比右边大的情况。

该过程如下图所示(来源:代码随想录):

 尤其注意第二次贪心时有两种选择:一个是candyVec[i + 1] + 1(从右边这个加1得到的糖果数量),一个是candyVec[i](之前比较右孩子大于左孩子得到的糖果数量)。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/947471.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号