贪心算法 - 学习笔记 【C++】

2024-12-09 - 第 38 篇
贪心算法 - 学习笔记
作者(Author): 郑龙浩 / 仟濹(CSND账号名)

贪心算法

学习课程:

https://www.bilibili.com/video/BV1f84y1i7mv/?spm_id_from=333.337.search-card.all.click&vd_source=2683707f584c21c57616cc6ce8454e2b

一、基本概念

贪心算法是一种在每个步骤都选择当前最优决策的算法。它只考虑当下的最好情况,希望通过这些局部最优选择来得到全局最优解。 特点是简单高效、具有无后效性。不过它不是万能的,有些情况不能通过它得到全局最优解,但在像哈夫曼编码这类有最优子结构的问题中很有效。

可以分为这四步( AI 生成 )

  • 分解问题:将一个复杂的大问题分解为一系列的子问题。例如在活动安排问题中,把安排所有活动这个大问题,分解为一个个单独活动的选择。
  • 确定贪心策略:找到一个合适的贪心策略,即确定在每个子问题中怎样选择才是局部最优。比如在找零问题中,贪心策略是优先使用面值大的硬币。
  • 按照策略进行选择:依据确定好的贪心策略,在每个子问题里做出选择。以活动安排为例,就按照结束时间最早这个策略来选择活动。
  • 合并结果:将所有子问题的局部最优解合并起来,得到最终的解。不过要注意这个解不一定是全局最优解。

简单一点讲就是:

  • 无论能否吞得下,能吞一点。
  • 根据当前的情况做出当前情况的最佳选择。
  • 当做出选择的时候,要永不改变。反之,回溯算法可以“反悔”。
  • 循环下去,使用“局部最优解”,逐步得到“整体最优解”

二、入门案例

海盗打劫商船,商船上装满古董,每件古董的重量不同,但每件古董的价值都相同,海盗船有最大载重的限制。

  • 假设 最大载重 500

问,最多装几件古董,既不翻船又获利最大?

思路:

// 思路:
// 1. 先排序,从小到大 --> 目的就是从最轻的算起,逐步往重的方放,直到放到放不下为止
// 2. 从 0 开始,逐步累加,直到累加和大于 500 为止 --> 意思就是,边放边计算数量,直到船上放不开。
// 先累加,然后判断累加后的是否 >  最大载重// 若 > 最大载重,就退出循环// 若 < 最大载重,就继续累加
#include <iostream>
#include <algorithm>
using namespace std;
int main( void ){int data[] = { 8, 20, 5, 80, 3, 420, 14, 330, 70 };int max = 500, ans = 0, sum = 0; // 最大载重, 古董数量, 总重量int cnt = sizeof( data ) / sizeof( data[ 0 ] ); // 计算数组元素个数sort( data, data + cnt ); // 排序for ( int i = 0; i < cnt; i ++ ){// 先累加,然后判断累加后的是否 >  最大载重// 若 > 最大载重,就退出循环// 若 < 最大载重,就继续累加sum += data[ i ]; // 总重量累加if( sum > max ) // 如果当前重量大于最大载重,就退出循环break;ans ++; // 古董数量累加}cout << "古董数量:" << ans << endl;return 0;
}

三、初级案例 - 字节跳动笔试题

一个很长的花坛,一部分地已经种植了花,另一部分却没有。
花不能种植在相邻的地块上,否则它们会争夺水源,两者都会死去。

给你一个整数数组表示花坛,0 表示没种植花,1 表示种植了花。

给定一个数n,能不能种下 n 朵花?

分析 + 思路:

  • 首先要知道:给定了一个数组(表示的花坛),我们需要在给定的数组中种花。Eg: 10001 --> 可以种一朵花。 10010 --> 不可以种花. 10000001 / 1000001 --> 可以种两朵花
  • 可以种花的条件是:当前位置的左边和右边都没有花。 Eg: 10001 --> 可以种一朵花。 10010 --> 不可以种花. 10000001 / 1000001 --> 可以种两朵花
  • 最终计算的是,n朵花是否能全部种到 给定的花坛中去(数组)
  1. 封装一个函数,用来判断 是否可以将 n 朵花 种完
    这个地方的判断需要特别注意,主要是 最两边的元素判断。
    特别容易出错。
  2. 函数过程:遍历数组,如果当前位置可以种花,则 将当前位置的元素变为 “1”,并且 种植数量 ++
  3. 判断 种植数量 是否等于 n ,如果等于 n ,则打印 Yes ,否则打印 No
// [字节跳动]笔试真题
// 一个很长的花坛,一部分地已经种植了花,另一部分却没有。
// 花不能种植在相邻的地块上,否则它们会争夺水源,两者都会死去。// 给你一个整数数组表示花坛,0 表示没种植花,1 表示种植了花。// **给定一个数n,能不能种下 n 朵花?**  能打印 Yes, 不能打印 No// 分析 + 思路:
// 首先要知道:给定了一个数组(表示的花坛),我们需要在给定的数组中种花。
// 可以种花的条件是:当前位置的左边和右边都没有花。 Eg: 10001 --> 可以种一朵花。 10010 --> 不可以种花. 10000001 / 1000001 --> 可以种两朵花 
// 最终计算的是,n朵花是否能全部种到 给定的花坛中去(数组)// 1. 封装一个函数,用来判断 是否可以将 n 朵花 种完
// 这个地方的判断需要特别注意,主要是 最两边的元素判断。
// 特别容易出错。
// 2. 函数过程:遍历数组,如果当前位置可以种花,则 将当前位置的元素变为 “1”,并且 种植数量 ++ 
// 3. 判断 种植数量 是否等于 n ,如果等于 n ,则打印 Yes ,否则打印 No#include <iostream>
#include <vector>
// 判断是否可以将 n 朵花 种完
bool canPlant( bool *data, int dataSize, int n ){ //  data 表示数组首地址 dataSize 表示 存储的数据的宽度 即花坛的长度, n 表示需要种植的花的数量int count = 0; // 表示 种植数量for( int i = 0; i < dataSize;  i ++ ){if( data[ i ] == 0 && data[ i - 1 ] == 0 && data[ i + 1 ] == 0 && i - 1 >= 0 && i + 1 < dataSize ){ // 种植条件: 左右两边都没有花 && 边界条件:不能越界data[ i ] = 1; // 种花count ++; // 种植数量 ++}if( count >= n )    return 1; // 如果 count 种植数量 已经满足了 n 朵花,无需再进行多余的循环}return 0;
}
using namespace std;
int main( void ){bool data[] = { 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1 };int n;cin >> n; // 输入种植几朵花if ( canPlant( data, sizeof(data) / sizeof(data[0]), n ) )cout << "Yes" << endl;elsecout << "No" << endl;return 0;
}

四、中级案例 - 华为笔试题

给定一个整数数组,表示在同一行的行星。

每一个元素,其绝对值表示行星的大小,正负表示行星的移动方向

正 表示向右移动, 负 表示向左移动.

每一颗行星 速度相同。

碰撞规则

  1. 两个行星碰撞,较小的行星会爆炸。
  2. 如果大小相同,则两颗都会爆炸。
  3. 两颗移动方向相同的行星,永远不会发生碰撞。

分析 + 思路

分析 + 思路:

  1. 封装一个函数,用来判断 + 计算 全部星球碰撞以后留下来的行星。并存入一个新的数组。

  2. 函数过程:两个变量分别表示的 左边的行星右边的行星

    几种情况:(1)为相同的情况;(2)(3)为不先相同的情况

    (1). 两个行星方向 【相同】,则永远不会发生碰撞

    (2). 两个行星方向 【相反】 && 左边质量 > 右边质量 --> 右边行星爆炸

    (3). 两个行星方向 【相反】 && 左边质量 < 右边质量 --> 左边行星爆炸

    (4). 两个行星方向 【相反】 && 左边质量 == 右边质量 --> 两个行星都爆炸

    注: 【情况(2)】 与 【情况(3)】【情况(4)】 合并为一种情况。

    循环 + 判断 完所有的情况以后,最后将留下来的行星 【存入】 新的数组中。

  3. 将留下来的行星质量存入 传来的数组参数中。

可能会有错误。

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;// 判断 + 计算 全部星球碰撞以后留下来的行星。并存入一个新的数组。
void newdata ( vector <int> &data, vector <int> &data2 ){// 使用 ector 就不用单独计算 数组的大小了// 参数: 原来容器, 容器大小, 新容器(存放星球爆炸以后的数据)int left = 0, right = 1; // 左边的星球 右边的星球;while( right < data.size()){ // 循环条件:右边的星球 【不超过】 星球容器宽度// 两个行星方向 【相同】,则永远不会发生碰撞if( data[left] * data[right] > 0 ){ // 方向一致,则永远不会发生碰撞【不爆炸】 【情况(1)】left = right;right ++;continue; // 没有爆炸,数据未更新,表示位置变量发生变化,重新进入循环// 注: 下面这种写法是有错误的,因为 left 并不一定 移动到 下一个星球 --> 可能会出现 bug// 只有 right 可以进行 ++// left++;// right++;} else if( data[left] * data[right] < 0 ){ // 两个行星方向 【相反】 --> 两种情况//【情况(2)】 --> 左边质量 > 右边质量 --> 右边行星爆炸if( abs(data[left]) > abs(data[right]) ){//【右边消失】data.at( right ) = 0; // 0 表示爆炸的星球left = right;right ++;continue; // 已经爆炸,数据更新,表示位置变量发生变化,重新进入循环} else if ( abs(data[left]) < abs(data[right]) )//【情况(3)】 左边质量 < 右边质量 --> 左边行星爆炸{ data.at( left ) = 0; // 0 表示爆炸的星球left = right;right ++;continue; // 已经爆炸,数据更新,表示位置变量发生变化,重新进入循环} else // 【情况(4)】左边质量 == 右边质量 --> 两个行星全都爆炸{data.at( left ) = 0; // 0 表示爆炸的星球data.at( right ) = 0; left = right;right ++;continue;}}// 判断 left 与 right 的指针位置已经爆炸if ( data[ left ] == 0 ) // 左边的星球已经爆炸{left = right;right ++;continue; // 没有爆炸,数据未更新,表示位置变量发生变化,重新进入循环}else if ( data[ right ] == 0 ){ // 右边的星球已经爆炸right ++; // 只需进行 右边位置移动即可continue;}// cout << right << endl;// cout << "123" << endl; // 测试死循环}// 将更新后的 为 "1" 的数据存入 新的容器中for( auto i = data.begin() ; i != data.end(); i++ ){if( *i!= 0 ){ // 将没爆炸的星球存入新的容器中data2.push_back( *i );}}
}
int main( void ){vector <int> data = {10, 2, -2, -2, -5}; // 随机写几个数据进行测试vector <int> data2; // 新的容器newdata ( data, data2 ); // 计算留下的星球cout << "爆炸个数:" << data.size() - data2.size() << endl; // 打印 爆炸的星球个数cout << "留下的星球:" << endl;for ( auto i = data2.begin(); i != data2.end(); i++ ){cout << *i << " "; // 打印 留下的星球}return 0;
}

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

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

相关文章

【安全研究】某黑产网站后台滲透与逆向分析

文章目录 x01. 前言x02. 分析 【&#x1f3e0;作者主页】&#xff1a;吴秋霖 【&#x1f4bc;作者介绍】&#xff1a;擅长爬虫与JS加密逆向分析&#xff01;Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长期坚守并致力于Python与爬虫领域研究与…

ESP32外设学习部分--SPI篇

SPI学习 前言 我个人以为开始学习一个新的单片机最好的方法就是先把他各个外设给跑一遍&#xff0c;整体了解一下他的功能&#xff0c;由此记录一下我学习ESP32外设的过程&#xff0c;防止以后忘记。 SPI 配置步骤 SPI总线初始化 spi_bus_config_t buscfg {.miso_io_num …

Viewer.js----图片查看

Viewer.js 是一个用于展示图片、视频等多媒体内容的轻量级 JavaScript 库&#xff0c;提供了现代的图片查看器功能&#xff0c;比如放大、缩小、旋转、拖动等操作&#xff0c;适用于浏览器中查看图片等资源。 官网地址&#xff1a;Viewer.js 详细说明&#xff1a;Viewer.js –…

禅道Bug的一次迁移

一、场景 平时工作记录在公司禅道上的问题想备份一份到本地&#xff0c;但是又没有公司禅道的数据库信息&#xff0c;有时候出测试报告想批量调整数据方便截图很困难&#xff0c;同时也为了学习禅道数据流转过程&#xff0c;所以有了把缺陷保存到本地一份的想法。 实际上禅道支…

[ShaderLab] 【Unity】【图像编程】理解 Unity Shader 的结构

在计算机图形学领域,开发者经常面临着管理着色器复杂性的挑战。正如大卫惠勒(David Wheeler)所说:“计算机科学中的任何问题都可以通过增加一层抽象来解决。” Unity 提供了这样一层抽象,即 ShaderLab,它通过组织和定义渲染过程的各个步骤,简化了编写着色器的过程。 什…

redis 怎么样查看list

在 Redis 中&#xff0c;可以通过以下方法查看列表的内容或属性&#xff1a; 1. 查看列表中的所有元素 使用 LRANGE 命令&#xff1a; LRANGE key start endkey 是列表的名称。start 是起始索引&#xff0c;0 表示第一个元素。end 是结束索引&#xff0c;-1 表示最后一个元素…

Redis - 消息队列 Stream

一、概述 消息队列 定义 消息队列模型&#xff1a;一种分布式系统中的消息传递方案&#xff0c;由消息队列、生产者和消费者组成消息队列&#xff1a;负责存储和管理消息的中间件&#xff0c;也称为消息代理&#xff08;Message Broker&#xff09;生产者&#xff1a;负责 产…

C语言数组和字符串笔记

C语言数组和字符串笔记 1. 数组及其相关概念 1.1 为什么需要使用数组&#xff1f; 数组是一个有序的、类型相同的数据集合。这些数据被称为数组的元素。每个数组都有一个名字&#xff0c;数组名代表数组的起始地址。数组的元素通过索引或下标访问&#xff0c;索引从0开始。 …

淘宝详情网页爬虫:技术解析与实战指南

引言 淘宝作为中国最大的电商平台之一&#xff0c;拥有海量的商品数据。对于开发者来说&#xff0c;获取淘宝商品详情接口是一个常见的需求。本文将介绍如何使用Python编写爬虫&#xff0c;获取淘宝商品详情信息&#xff0c;并探讨在实际应用中可能遇到的挑战与解决方案。 环…

双目摄像头标定方法

打开matlab 找到这个标定 将双目左右目拍的图像上传&#xff08;左右目最好不少于20张&#xff09; 等待即可 此时已经完成标定&#xff0c;左下角为反投影误差&#xff0c;右边为外参可视化 把这些误差大的删除即可。 点击导出 此时回到主页面&#xff0c;即可看到成功导出 Ca…

数据结构开始——时间复杂度和空间复杂度知识点笔记总结

好了&#xff0c;经过了漫长的时间学习c语言语法知识&#xff0c;现在我们到了数据结构的学习。 首先&#xff0c;我们得思考一下 什么是数据结构&#xff1f; 数据结构(Data Structure)是计算机存储、组织数据的方式&#xff0c;指相互之间存在一种或多种特定关系的数据元素…

什么是MMD Maximum Mean Discrepancy 最大均值差异?

9多次在迁移学习看到了&#xff0c;居然还是Bernhard Schlkopf大佬的论文&#xff0c;仔细看看。 一.什么是MMD&#xff1f; 1. MMD要做什么&#xff1f; 判断两个样本&#xff08;族&#xff09;是不是来自于同一分布 2.怎么做&#xff1f;&#xff08;直观上&#xff09;…

计算机操作系统虚拟化与云计算教学指南

一、引言 在当今数字化时代&#xff0c;计算机操作系统虚拟化和云计算已成为信息技术领域的核心技术。无论是大型企业的数据中心&#xff0c;还是个人开发者的工作环境&#xff0c;都广泛应用着这些技术。本教学博客将深入探讨虚拟化的基本概念、虚拟化技术、云计算等重要内容…

电梯内电动车识别数据集,可准确识别电梯内是否有电动车 支持YOLO,COCO,VOC三种格式的标注 7111张图片

电梯内电动车识别数据集&#xff0c;可识别电梯内是否有电动车 支持YOLO&#xff0c;COCO&#xff0c;VOC三种格式的标注 7111张图片 7111总图像数 数据集分割 训练组 74&#xff05; 5291图片 有效集 16% 1168图片 测试集 9&#xff05; 652…

Collection接口

目录 一. Collection基本介绍 二. Collection中的方法及其使用 1. 添加元素 (1) 添加单个元素 (2) 添加另一集合中的所有元素 2. 删除元素 (1) 删除单个元素 (2) 删除某个集合中包含在其他集合中的元素 (3) 保留两个集合中的交集部分, 删除其他元素. 3. 遍历元素 (1) …

Mybatis Plus 3.0 快速入门

1、简介 MyBatis-Plus (简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。 2、创建并初始化数据库 2.1、创建数据库 mybatis_plus 2.2、创建 User 表 其表结构如下: idnameageemail1Jone18test1@baomidou.com2Jack…

图像清晰度计算

对于单图清晰度检测&#xff0c;可以采用基于梯度的方法来评估图像的清晰度。这些方法通过计算图像中边缘信息的强度来量化图像是否足够清晰。以下是几种常用的技术及其具体实现方式&#xff0c;特别适用于单张图片的清晰度检测。 拉普拉斯变换&#xff08;Laplacian&#xff…

Verilog实现图像处理的行缓存Line Buffer

在图像处理中&#xff0c;难免会遇到对图像进行卷积或者模板的局部处理&#xff0c;例如ISP中的一些算法&#xff0c;很大部分都需要一个窗口&#xff0c;在实时视频处理中&#xff0c;可以利用行缓存Line buffer可以暂存几行数据&#xff0c;然后同时输出每行中的对应列的像素…

【银河麒麟高级服务器操作系统】有关dd及cp测试差异的现象分析详解

了解更多银河麒麟操作系统全新产品&#xff0c;请点击访问 麒麟软件产品专区&#xff1a;https://product.kylinos.cn 开发者专区&#xff1a;https://developer.kylinos.cn 文档中心&#xff1a;https://documentkylinos.cn dd现象 使用银河麒麟高级服务器操作系统执行两次…

C++ 中面向对象编程如何处理异常?

一、引言 在 C 编程中&#xff0c;异常处理是一项重要的技术&#xff0c;它可以帮助我们更好地管理程序中的错误情况&#xff0c;提高程序的稳定性和可靠性。特别是在面向对象编程中&#xff0c;异常处理更是不可或缺的一部分。本文将介绍 C 中面向对象编程如何处理异常&#…