【贪心算法】C++ 解决算法题:买卖股票 / K次取反 / 按身高排序 / 优势洗牌

news2024/11/13 3:46:23

1. 前言

1.1 贪心算法介绍

贪心算法(Greedy Algorithm)是一种在每一步选择中都采取当前状态下最优决策的算法。贪心算法通常用来解决最优化问题,其核心思想是通过局部最优解逐步推导出全局最优解。

在贪心算法中,我们并不总是考虑到未来可能发生的情况,而是只关注当前的最优选择。这种贪心选择性质使得贪心算法特别适合解决那些具有最优子结构性质的问题,即局部最优解能够推导出全局最优解的问题。

贪心算法的基本思路可以总结为以下几步:

  1. 确定问题的最优子结构:问题的最优解可以通过子问题的最优解逐步推导得到。
  2. 构造贪心选择:在每一步都做出当前状态下的最优选择,即局部最优解。
  3. 证明贪心选择性质:证明每一步的贪心选择都是最优的,能够推导出全局最优解。

需要注意的是,贪心算法并不适用于所有的问题,因为并非所有问题都具有最优子结构性质。在某些情况下,贪心算法得到的结果可能并不是全局最优解,而只是一个较好的解。因此,在应用贪心算法时,需要仔细分析问题的特性,以确定贪心算法是否适用于该问题。

下面会介绍一些用贪心解决的算法题:

2. 算法题

2.1_买卖股票的最佳时机

在这里插入图片描述
分析:

  • 买卖股票类的问题首先有一个通用解法,就是动态规划(之前有写),这里用贪心解:
  • 题目要求最多只能进行一次交易,因此我们可以利用循环解题

思路:

  • 创建两个变量ret负责记录统计过的当前最大利润,以及一个minPrev用于记录最小的买入价格
  • 通过遍历数组,每次对两变量进行更新,最后的ret就是结果

代码:

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int ret = 0;
        for(int i = 0, minPrev = INT_MAX; i < prices.size(); ++i)
        {
            ret = max(ret, prices[i] - minPrev); // 更新结果
            minPrev = min(minPrev, prices[i]);
        }

        return ret;
    }
};

2.2_买卖股票的最佳时机II

在这里插入图片描述

分析:

  • 本题与上一题的区别在于:上一题只有一次交易机会本题可以多次交易,但是不能同时进行多笔交易,必须保证当前未持股时才能买入。

思路:
在这里插入图片描述

  • 我们只要保证每次做到低进高出即可,即在递增起点买入,终点卖出;
    此时有两种解决方法:
    • ① 以天数为指标:遍历数组,只要出现递增,那么就将这段递增的值加入到结果ret中
    • ② 利用双指针:本质是一样的,int i遍历数组,当找到一处递增时,用指针j将该递增区间统计,当走出递增时,将i和j间的距离就是这一段的递增值(利润)

代码:

  • 利用天数:
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        // 利用天计算
        int ret = 0, n = prices.size();
        for(int i = 1; i < n; ++i)
        {
            if(prices[i] > prices[i-1])
                ret += prices[i] - prices[i-1];
        }

        return ret;
    }
};
  • 利用双指针:
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        // 利用双指针
        int ret = 0, n = prices.size();
        for(int i = 0; i < n; ++i)
        {
            int j = i;
            while(j + 1 < n && prices[j+1] > prices[j])
                j++;
            ret += prices[j] - prices[i];
            i = j; // 更新i到j+1
        }

        return ret;
    }
};

2.3_K次取反后最大化的数组和

在这里插入图片描述

分析:

  • 题目要求找到对整数数组nums的元素进行k次取反操作后的最大和,自然要先对负数取反,其次再对罪小的正数进行取反(贪心)

思路:

  • 首先统计负数的个数,先将可以反转的负从最小的进行开始反转,如果所有负数均反转,且仍k>0
  • 此时从小到大反转正数
  • (这个过程也可以通过一个priority_queue来完成)

代码:

class Solution {
public:
    int largestSumAfterKNegations(vector<int>& nums, int k) {
        int n = nums.size(), minElem = INT_MAX;
        int m = 0;
        for(auto x : nums) // 统计负数个数m
        {
            if(x < 0) ++m;
            minElem = min(minElem, abs(x)); // 找数组中绝对值最小的数
        }

        int ret = 0;
        if(m > k)
        {
            sort(nums.begin(), nums.end());
            // 将前k小的负数转正,并统计到ret
            for(int i = 0; i < k; ++i)
                ret += abs(nums[i]);

            for(int i = k; i < n; ++i)
                ret += nums[i];
        }
        else // m == k: 将所有负数转正
        {
            // m < k:
            // 先将所有负数转正;后根据k-m的奇偶性进行编写
            for(auto x : nums)
                ret += abs(x);

            if((k - m) % 2) // 奇数 // 将当前数组的绝对值最小逆号
                ret -= minElem * 2;
        }

        return ret;
    }
};

2.4_按身高排序

在这里插入图片描述

分析:

  • 本质就是根据数组2的数据对数组1进行排序,可以利用vector 以及 pair<type, type> 二元组来完成;也可以创建一个下标数组,根据身高对下标数组进行排序,最后直接输出names[index[i]]

思路:

  • ① 创建一个vector<pair<int, string>> people,再将身高信息和姓名信息存放到该people进行降序排序
  • ② 创建并初始化一个下标映射的数组index,排序index(利用lambda表达式)根据身高信息逆序排序,最后返回姓名names[index[i]]

代码:

  • 利用二元组:
class Solution {
public:
    vector<string> sortPeople(vector<string>& names, vector<int>& heights) {
        // 利用二元组
        int n = names.size();
        vector<pair<int, string>> people;
        for (int i = 0; i < n; ++i) {
            people.push_back(make_pair(heights[i], names[i]));
        }

        // 根据身高降序
        sort(people.rbegin(), people.rend());

        // 提取结果
        vector<string> ret;
        for (const auto& p : people) {
            ret.push_back(p.second);
        }
        return ret;
    }
};

  • 利用下标数组:
class Solution {
public:
    vector<string> sortPeople(vector<string>& names, vector<int>& heights) {
        // 根据身高对下标进行排序: 
        // 创建下标数组
        int n = heights.size();
        vector<int> index(n);
        for(int i = 0; i < n; ++i)
            index[i] = i;

        // 排序数组
        sort(index.begin(), index.end(), [&](const int i, const int j){
            return heights[i] > heights[j];
        });

        vector<string> ret;
        // 提取结果到数组
        for(int i = 0; i < n; ++i)
        {
            ret.push_back(names[index[i]]);
        }
        return ret;
    }
};

2.5_优势洗牌

在这里插入图片描述

分析:

  • 要使优势最大,则应该尽量使用较小的大值(在nums1中找大于nums2[i]的最小值),类似田忌赛马的排列规则

思路:

  1. 首先对nums1进行排序(便于找大于nums2[i]的最小值),由于要根据nums2更改nums1,所以不能排序nums2,创建一个下标数组并根据nums2进行排序,就可以利用index按大小顺序访问nums2的元素
  2. 创建ret,用于存放nums1改变后的位置
  3. 遍历nums1,此时的思路就是田忌赛马的思路,对于当前元素(nums1未匹配的最小元素):
    • 如果大于nums2[index2[left]](当前最小元素),就直接匹配该元素
    • 如果小于nums2[index2[left]],那就匹配nums2的最大的元素

代码:

class Solution {
public:
    vector<int> advantageCount(vector<int>& nums1, vector<int>& nums2) {
        // 1. 排序数组
        int n = nums1.size();
        sort(nums1.begin(), nums1.end());
        // 1.5 创建下标数组
        vector<int> index2(n);
        for(int i = 0; i < n; ++i)
            index2[i] = i;
        sort(index2.begin(), index2.end(), [&](const int i, const int j){
            return nums2[i] < nums2[j]; 
        });

        // 2. 田忌赛马: 如果当前比得过,则插入当前位置
        // 如果比不过,则匹配对方最大的
        int left = 0, right = n-1;
        vector<int> ret(n);
        for(auto x : nums1)
        {
            if(x > nums2[index2[left]]) ret[index2[left++]] = x;
            else ret[index2[right--]] = x;
        }

        return ret;
    }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1712063.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

探索电商ERP平台的功能架构:实现高效运营的关键

在当今数字化时代&#xff0c;电子商务已经成为了商业运营的主流模式之一。为了应对日益激烈的市场竞争&#xff0c;企业需要借助先进的技术工具来提高运营效率和管理能力。在这篇博客中&#xff0c;我们将深入探讨电商ERP平台的功能架构&#xff0c;揭示其如何成为实现高效运营…

港科夜闻|香港科大与利丰成立供应链研究院,应对多变商业环境中的挑战与机遇...

关注并星标 每周阅读港科夜闻 建立新视野 开启新思维 1、香港科大与利丰成立供应链研究院&#xff0c;应对多变商业环境中的挑战与机遇。该研究院的成立&#xff0c;旨在推动全球供应链管理的研究与创新商业模式。研究院结合香港科大卓越研究实力及利丰于供应链管理方面的深厚行…

Dify快速接入微信

一、Dify简介 项目官网&#xff1a;Dify.AI 生成式 AI 应用创新引擎 Dify 是一款开源的大语言模型(LLM) 应用开发平台。它融合了后端即服务&#xff08;Backend as Service&#xff09;和 LLMOps 的理念&#xff0c;使开发者可以快速搭建生产级的生成式 AI 应用。即使你是非…

【管理咨询宝藏116】某大型国有集团公司战略落地保障方案

本报告首发于公号“管理咨询宝藏”&#xff0c;如需阅读完整版报告内容&#xff0c;请查阅公号“管理咨询宝藏”。 【管理咨询宝藏116】某大型国有集团公司战略落地保障方案 【格式】PDF版本 【关键词】战略落地、大型国企、战略报告 【核心观点】 - 资产规模以提高资产质量、…

halcon SVM 缺陷检测分类

一、概述 训练数据 二、算子解释 compactness Halcon 算子 compactness_halcon compactness-CSDN博客 *计算输入区域的紧凑度 compactness (Region, Compactness) 原理解释 convexity 每个输入区域的凸度 Halcon 算子 convexity_halcon convexity-CSDN博客 *计算每个输…

如何关闭MySQL凌晨12点自动弹窗?

要关闭 MySQL 在凌晨 12 点自动弹窗的行为&#xff0c;首先需要确定弹窗的具体原因。 打开“任务计划程序”&#xff1a; 按 Win R&#xff0c;输入 taskschd.msc&#xff0c;然后按 Enter。 在左侧导航栏中&#xff0c;选择“任务计划程序库”。 查找与 MySQL 相关的任务&…

12-常用类

1. 包装类 针对八种基本数据类型封装的相应的引用类型。 有了类的特点&#xff0c;就可以调用类中的方法。&#xff08;为什么要封装&#xff09; 基本数据类型包装类booleanBooleanchar CharacterbyteByteshortShortintIntegerlongLongfloatFloatdoubleDouble 1.1 …

LeetCode 518.零钱兑换Ⅱ

思路&#xff1a; 这题和之前做的不大一样&#xff0c;之前的动态规划转化成背包问题一般都是求能放入的最大重量&#xff0c;这个是求组合数。 求组合数的状态转移方程之前在1和0提到过&#xff1a; dp[j]dp[j-nums[]i]; 这里重点分析一下遍历顺序&#xff1a; 这段代码里面是…

算法学习笔记(7)-贪心算法

##什么是贪心算法 一种常见的解决优化类型的问题&#xff0c;基本的思想是在问题的每个决策阶段&#xff0c;都选择当前看起来最优的选择&#xff0c;即贪心地做出局部最优解的决策&#xff0c;以期待获得全局最优解。 ##贪心算法与动态规划的区别&#xff08;二者都为解决优化…

苏洵,大器晚成的家风塑造者

&#x1f4a1; 如果想阅读最新的文章&#xff0c;或者有技术问题需要交流和沟通&#xff0c;可搜索并关注微信公众号“希望睿智”。 苏洵&#xff0c;字明允&#xff0c;号老泉&#xff0c;生于宋真宗大中祥符二年&#xff08;公元1009年&#xff09;&#xff0c;卒于宋英宗治平…

linux解压.zip文件命令详解

在Linux中解压.zip文件&#xff0c;你可以使用unzip命令。如果你的系统中没有安装unzip&#xff0c;你可以使用包管理器来安装它。 Debian的系统&#xff08;Ubuntu&#xff09;&#xff0c;安装命令&#xff1a; sudo apt-get install unzip基于Red Hat的系统&#xff08;如…

想让企业“火力全开”?找六西格玛培训公司就对了!

在如今的市场环境中&#xff0c;企业面临着不断变化的挑战和无限的可能。要在这场竞争中独领风骚&#xff0c;实现稳健的增长&#xff0c;六西格玛作为一种以数据驱动的管理理论&#xff0c;提供了实际可行的解决方案。六西格玛培训公司&#xff0c;作为这一领域的专家&#xf…

广东海上丝绸之路文化促进会正式批复荐世界酒中国菜的指导单位

广东海上丝绸之路文化促进会正式批复成为“世界酒中国菜”系列活动指导单位 近日&#xff0c;广东海上丝绸之路文化促进会近日正式批复荐酒师国际认证&#xff08;广州&#xff09;有限公司&#xff0c;成为备受瞩目的“世界酒中国菜”系列活动的指导单位。此举旨在通过双方的…

香橙派OrangePI AiPro测评

实物 为AI而生 打开盒子 截图电源开机进入 作为一个AI产品,必须有一个人机交互的界面才行。大家都在跑算法,于是我就开始进行整理着手整理搭建Qt的环境。 1、下载源码 wget https://download.qt.io/archive/qt/5.12/5.12.12/single/qt-everywhere-src-5.12.12.tar.xz待…

网吧|基于SprinBoot+vue的网吧管理系统(源码+数据库+文档)

网吧管理系统 目录 基于SprinBootvue的网吧管理系统 一、前言 二、系统设计 三、系统功能设计 1 管理员功能模块 2 网管功能模块 3 会员功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#…

C++之类(class)的三种访问修饰符(public、private、protected)----成员变量与函数权限

1、背景介绍 在C中&#xff0c;类&#xff08;class&#xff09;的三种访问修饰符&#xff08;access specifiers&#xff09;用于控制类的成员&#xff08;属性和方法&#xff09;的访问权限。这些修饰符决定了类成员在类的外部是否可以被访问。以下是这三种访问修饰符的详细…

软件架构设计之质量属性浅析

引言 在数字化浪潮席卷而来的今天&#xff0c;软件已经渗透到我们生活的方方面面&#xff0c;从手机APP到大型企业级系统&#xff0c;无一不彰显着软件技术的魅力。然而&#xff0c;在这背后&#xff0c;软件架构设计作为软件开发的基石&#xff0c;其质量属性的重要性不言而喻…

前端开发工程师——AngularJS

一.表达式和语句 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice-w…

win10修改conda环境和缓存默认路径

win10修改conda环境和缓存默认路径 conda环境和缓存的默认路径&#xff08;envs directories 和 package cache&#xff09;不一定要默认存储在用户目录&#xff0c;我们可以将他们设置到盈余空间稍大的其他目录来缓解这种空间压力&#xff0c;只要保证不同用户之间的设置不同…

【2】:向量与矩阵

向量 既有大小又有方向的量叫做向量 向量的模 向量的长度 单位向量 (只表示方向不表示长度) 向量的加减运算 向量求和 行向量与列向量的置换 图形学中竖着写 向量的长度计算 点乘&#xff08;计算向量间夹角&#xff09; 点乘满足的运算规律 交换律、结合律、分配…