【3.3】贪心算法-解分发糖果

news2024/11/23 5:04:18

一、题目

        老师想给孩子们分发糖果,有N 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分。
        你需要按照以下要求,帮助老师给这些孩子分发糖果:
        1)每个孩子至少分配到1 个糖果。
        2)评分更高的孩子必须比他两侧的邻位孩子获得更多的糖果。
        那么这样下来,老师至少需要准备多少颗糖果呢?

示例 1:
输入:[ 1 , 0 , 2 ]
输出:5
解释:你可以分别给这三个孩子分发2、1、2颗糖果。

示例 2:
输入:[ 1 , 2 , 2 ]
输出:4
解释:你可以分别给这三个孩子分发1、2、1颗糖果。第三个孩子只得到1颗糖果,这已满足上述两个条件。

二、解题思路

这个问题涉及三个关键条件:
1. 每个孩子至少获得一个糖果。
2. 得分高于相邻孩子的孩子应得到更多的糖果。
3. 计算出满足上述条件所需的最少糖果总数。

解决步骤如下:
首先,为了同时满足条件1和条件3,我们可以简单地给每个孩子分配一个糖果。
其次,为了满足条件2和条件3,我们需要进一步调整糖果的分配:
        1)如果一个孩子的得分仅高于其左侧的孩子,那么他应比左侧的孩子多获得一个糖果。
        2)如果一个孩子的得分仅高于其右侧的孩子,那么他应比右侧的孩子多获得一个糖果。
        3)如果一个孩子的得分同时高于其左右两侧的孩子,那么他应比左右两侧孩子中获得糖果较多的那个孩子再多获得一个糖果。

为了实现上述调整,我们可以采用两次遍历的方法:
        第一次遍历从左到右进行,确保每个得分高于其左侧邻居的孩子获得比左侧孩子多一个糖果。这样处理后,每个孩子都满足了其右侧邻居得分高于自己时的情况。
        第二次遍历从右到左进行,确保每个得分高于其右侧邻居的孩子获得比右侧孩子多一个糖果。通过这次遍历,我们进一步满足了每个孩子左侧邻居得分高于自己时的情况。

        通过这两次遍历,我们可以确保每个孩子都根据其得分获得了正确的糖果数量,从而满足了所有条件并计算出了所需的最少糖果总数。随便举个例子画个图来看一下

三、代码实现

原理简单,来看看代码:

#include <iostream>
#include <vector>
#include <algorithm>

int candy(std::vector<int>& ratings) {
    int length = ratings.size();
    // 记录的是从左往右循环的结果
    std::vector<int> left(length, 1);
    // 记录的是从右往左循环的结果
    std::vector<int> right(length, 1);

    // 先从左往右遍历,如果当前孩子的得分比左边的高,
    // 那么当前孩子的糖果要比左边孩子的多一个
    for (int i = 1; i < length; i++) {
        if (ratings[i] > ratings[i - 1]) {
            left[i] = left[i - 1] + 1;
        }
    }

    // 然后再从右往左遍历,如果当前孩子的得分比右边的高,
    // 那么当前孩子的糖果要比右边边孩子的多一个
    for (int i = length - 2; i >= 0; i--) {
        if (ratings[i] > ratings[i + 1]) {
            right[i] = right[i + 1] + 1;
        }
    }

    // 要满足左右两边的条件,那么当前孩子的糖果就要取
    // 从左往右和从右往左的最大值。
    int total = 0;
    for (int i = 0; i < length; i++) {
        // 累计每个孩子的糖果
        total += std::max(left[i], right[i]);
    }

    return total;
}

int main() {
    std::vector<int> ratings = {1, 0, 2};
    int result = candy(ratings);
    std::cout << "Minimum number of candies: " << result << std::endl;
    return 0;
}
        其实还可以优化一下,在从右往左遍历的时候就可以统计total 的值了,不需要再使用第 3个 for 循环,代码如下
#include <iostream>
#include <vector>
#include <algorithm>

int candy(std::vector<int>& ratings) {
    int length = ratings.size();
    std::vector<int> count(length, 1); // 默认给每个孩子一个糖果

    // 先从左往右遍历
    for (int i = 1; i < length; i++) {
        if (ratings[i] > ratings[i - 1]) {
            count[i] = count[i - 1] + 1;
        }
    }

    // 从右往左遍历,然后顺便累加total的值
    int total = count[length - 1]; // total的默认值就是最后那个孩子的糖果数量
    for (int i = length - 2; i >= 0; i--) {
        if (ratings[i] > ratings[i + 1]) {
            count[i] = std::max(count[i], count[i + 1] + 1);
        }
        total += count[i];
    }

    return total;
}

int main() {
    std::vector<int> ratings = {1, 0, 2};
    int result = candy(ratings);
    std::cout << "Minimum number of candies: " << result << std::endl;
    return 0;
}

        我们也可以通过一次从左到右的遍历来解决这个问题。在遍历过程中,我们需要考虑三种情况来确定当前孩子的糖果数量:

1. **递增情况**:如果当前孩子的评分比左边的孩子高,那么当前孩子的糖果数量应该比左边的孩子多一个。
2. **相等情况**:如果当前孩子的评分与左边的孩子相同,我们暂时将当前孩子的糖果数量设为1,但这个值可能会在后续的遍历中调整。
3. **递减情况**:如果当前孩子的评分比左边的孩子低,我们无法立即确定当前孩子的糖果数量。不过,我们可以统计递减序列的长度,因为递减序列的最后一个孩子的糖果数量必定是1,并且从后往前糖果数量逐渐增加。

为了更好地理解,我们以数组 `[1, 3, 4, 6, 8, 5, 3, 1]` 为例:

- 从左到右,评分递增的部分是 `[1, 3, 4, 6, 8]`,对应的糖果数量是 `[1, 2, 3, 4, 5]`。
- 评分递减的部分是 `[8, 5, 3, 1]`,对应的糖果数量是 `[5, 3, 2, 1]`。

最高点8的糖果数量是左右两边的最大值加1,即 `5 + 1 = 6`。

计算总糖果数量时,递增部分的糖果数量是 `(1 + 5) * 5 / 2 = 15`,递减部分的糖果数量是 `(1 + 3) * 3 / 2 = 6`,加上最高点8的糖果数量,总糖果数量是 `15 + 6 + 6 = 27`。

通过这种方式,我们可以有效地计算出满足所有条件的总糖果数量。

上面的数组的单调性用图像来表示就是

但实际上数组不一定都是这样的,有可能是这样的,比如 [2,5,7,4,3,1,5,7,8,6,4,3]

        这种情况下,我们可以将数组拆分为两个子数组 `[2, 5, 7, 4, 3, 1]` 和 `[1, 5, 7, 8, 6, 4, 3]` 来分别进行计算。需要注意的是,元素 `1` 被包含在了两个子数组中,这意味着在计算总糖果数量时,元素 `1` 会被重复计算两次。因此,在最终求和时,我们需要减去一次元素 `1` 的糖果数量,以确保总糖果数量是正确的。 我们来看下代码
#include <iostream>
#include <vector>

int candy(std::vector<int>& ratings) {
    int length = ratings.size();
    if (length == 1)
        return 1;

    int index = 0;
    int total = 0;

    while (index < length - 1) {
        int left = 0;
        // 统计递增的长度
        while (index < length - 1 && ratings[index + 1] > ratings[index]) {
            index++;
            left++;
        }

        int right = 0;
        // 统计递减的长度
        while (index < length - 1 && ratings[index + 1] < ratings[index]) {
            index++;
            right++;
        }

        // 记录顶点的值,也就是左右两边最大的值加1
        int peekCount = std::max(left, right) + 1;

        // 注意这里如果total不等于0要减1,是因为把数组拆分成子数组的时候,低谷的那个会被拆到两个数组中,
        // 如果total不等于0,说明之前已经统计过,而下面会再次统计,所以要提前减去。
        if (total != 0)
            total--;

        // 当前这个子数组的糖果数量就是前面递增的加上后面递减的然后在加上顶点的。
        total += (1 + left) * left / 2 + (1 + right) * right / 2 + peekCount;

        // 如果当前孩子的得分和前一个一样,我们让他降为1
        while (index < length - 1 && ratings[index + 1] == ratings[index]) {
            index++;
            total++;
        }
    }

    return total;
}

int main() {
    std::vector<int> ratings = {2, 5, 7, 4, 3, 1, 5, 7, 8, 6, 4, 3};
    int result = candy(ratings);
    std::cout << "Total candies: " << result << std::endl;
    return 0;
}

        贪心算法与动态规划不同之处在于,贪心算法在每一步都选择当前看起来最优的解决方案,而不考虑全局情况。对于这个问题,我们可以采用贪心策略:如果一个孩子的得分比他右边的孩子高,那么他应该得到比右边孩子多一个糖果;如果得分不比右边孩子高,那么他至少应该得到一个糖果。这样处理后,我们还需要从左到右再检查一遍,确保每个孩子的得分比他左边的孩子高时,他得到的糖果也比左边孩子多。通过这种局部最优的选择,我们可以确保满足题目要求的同时,使用的糖果数量最少。

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

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

相关文章

apache经典模型和nginx参数配置

Apache 几个经典的 Web 服务端 ①Apache prefork 模型 ● 预派生模式&#xff0c;有一个主控制进程&#xff0c;然后生成多个子进程&#xff0c;使用select模型&#xff0c;最大并发 1024 ● 每个子进程有一个独立的线程响应用户请求 ● 相对比较占用内存&#xf…

Cyberchef实用功能之-模拟沙箱的文件静态分析能力

本文将介绍如何使用cyberchef 提取文件的重要元数据的信息&#xff0c;根据自己的需求实现沙箱中的静态文件的分析能力。 在网络安全日常的运营&#xff0c;护网行动&#xff0c;重保活动的过程中&#xff0c;样本的分析是一个重要的过程&#xff0c;这些可疑或者恶意的样本的…

Beyond Compare忽略特定格式文本

1 问题背景&#xff1a; 文本对比时忽略某些文本。比如有些生成的文件需要做差异对比&#xff0c;除了内容有差异外&#xff0c;自动生成的ID也不同&#xff0c;想忽略这些ID。特别是文件内容比较多的时候。 如上图&#xff0c;其中UUID“*”的部分我想忽略。 方法&#xff1…

微信开放平台应用签名MD5

可以使用JS转换一下 一、在任一网页 右键 检查 打开调试 二、把字母转换成小写&#xff0c;去除&#xff08;:&#xff09; MD5 应用签名示例 "70:71:5F:CA:AE:E5:B1:01:30:11:8F".toLowerCase().replace(/:/g, "") 70715fcaaee5b10130118f 就是要填写…

我们为什么推出数据模型

调用接口之前&#xff0c;要写大量的字段校验代码&#xff1f; 需要关联读写多张表的数据&#xff0c;难以写出复杂的数据库语法&#xff1f; 不仅需要开发核心的业务逻辑&#xff0c;还需要加班搭建CMS和数据管理页面&#xff1f; 数据不止放在云开发&#xff0c;还需要查询…

ctfhub-web-基础认证

HTTP协议&#xff1a;什么是HTTP基本认证_网易订阅 https://zhuanlan.zhihu.com/p/64584734 弹框输入密码账号bp抓包&#xff0c;发现了Authorization&#xff1a;后面有一串BASE64的编码(输入的密码) 账号默认是admin&#xff0c;抓包后是basic 把抓到的包发送到爆破模块i…

MySql【约束】

约束 1.1 约束 是什么? 约束,即限制,就是通过设置约束,可以限制对数据表数据的插入,删除,更新 怎么做? 约束设置的语法,大部分是 create table 表名( 字段 数据类型(长度) 约束, 字段 数据类型(长度) 约束 ); 另外一种是建表后,使用alter语句修改表添加约束 1.1 数据类型 其实…

人社大赛算法赛题解题思路分享+第五名

关联比赛: [国家社保]全国社会保险大数据应用创新大赛 赛题背景分析及理解 本次比赛&#xff0c;“精准社保”的赛题为“基本医疗保险医疗服务智能监控”&#xff0c;由参赛队完成数据算法模型的开发设计&#xff0c;实现对各类医疗保险基金欺诈违规行为的准确识别。 在进行了…

AI工业ros机械臂

1、基本介绍 该产品是一款面向于人工智能、机器人工程等专业的实验平台&#xff0c;能够学习基于人工智能技术的智能机器臂相关知识。主要由工业六轴机械臂、机械臂控制器、边缘计算主机、安全防护工作台四部分构成。该产品可完成的课程&#xff1a;机器视觉、机器人操作系统RO…

Stable Diffusion 使用详解(10)--- 场景立体字

目录 背景 复刻立体雏形 Lora 模型 参数设置与controlnet 出图效果 融入图片 提取 合成 背景 虽然都是字&#xff0c;带场景的立体字和上一节讲的做法不太一样。一般来说&#xff0c;一般这种带字体的场景字现阶段都是Lora模型居多&#xff0c;Lora 属于轻量级模型&…

linux系统中USB模块基本原理分析

大家好,今天主要给大家分享一下,USB设备的发展历程。 第一:USB发展变化 随着时代的发展,USB模块也随之不断的升级。 USB1.1:规范了USB低全速传输; USB2.0:规范了USB高速传输,采用NRZI(反向不归零)编码(NRZI采用8bit编码方式),位填充(在数据进行NRZI编码前…

每日一练-threejs实现三维动态热力图

前言&#xff1a;学习自用Three.js搞个炫酷热力山丘图&#xff0c;作者讲解的十分详细&#xff0c;在这里不再过多赘述&#xff0c;直接上代码&#xff01; <template><div class"map" ref"map"></div> </template><script set…

亿发工单管理系统助力五金行业智造升级:高效生产新篇章

在五金制造行业&#xff0c;效率和质量决定了企业的竞争力。五金厂通过引入先进的工单管理系统&#xff0c;成功实现了从传统制造向智能制造的转型。今天&#xff0c;我们将带您深入了解这场变革背后的力量&#xff0c;揭示工单管理系统如何在五金工厂的各个环节中发挥重要作用…

Redis的哨兵(高可用)

实验环境&#xff1a;用一主两从来实现Redis的高可用架构。 一、Redis哨兵 Sentinel 进程是用于监控redis集群中Master主服务器工作的状态&#xff0c;在Master主服务器发生故障的时候&#xff0c; 可以实现Master和Slave服务器的切换&#xff0c;保证系统的高可用&#xff0c…

java一键生成数据库说明文档html格式

要验收项目了&#xff0c;要写数据库文档&#xff0c;一大堆表太费劲了&#xff0c;直接生成一个吧&#xff0c;本来想用个别人的轮子&#xff0c;网上看了几个&#xff0c;感觉效果不怎么好&#xff0c;自己动手写一个吧。抽空再把字典表补充进去就OK了 先看效果&#xff1a; …

找不到msvcp140.dll无法继续执行代码的基本解决策略,快速修复msvcp140.dll错误文件

在我们日常使用电脑的过程中&#xff0c;偶尔会遇到一些令人头疼的技术问题。其中&#xff0c;"找不到 msvcp140.dll 无法继续执行代码" 的错误提示便是许多 Windows 用户常见的难题之一。这条错误消息通常在尝试启动某些程序时出现&#xff0c;指出系统缺少一个关键…

SAP HR 逻辑数据库PNP简单说明

逻辑数据库是专门在逻辑数据库生成器中编辑的开发对象&#xff0c;其核心价值在于为其他ABAP程序提供来自分层树结构节点的数据访问能力。尽管在SAP的最新帮助文档中&#xff0c;逻辑数据库被标记为已过时的&#xff0c;但是在HR模块中仍然被广泛使用&#xff0c;提供读取和处理…

基于Echarts的大屏可视化

效果图 基于vue3echartsTailwind CSS技术实现 实现思路 新建一个新项目 找任一目录进行 pnpm create vitelatest选择vue和js开始项目 导入tailwindcss https://tailwindcss.com/ 选择vue按照里面的步骤一步一步完成即可 将事先准备好的资料导入到assets包中即可 写入项…

Android setText不生效问题(文字不显示)

1.直接说解决方案&#xff1a; 在代码没问题的情况下&#xff0c;将你的TextView的Id改一下&#xff0c;然后再重启编译器即可(注意&#xff0c;不修改TextView的ID&#xff0c;单独重启是没有作用的&#xff01;) 2.出现问题的过程&#xff1a; 新增的一个页面与之前做好的界…

“workon不是内部命令“/virtualenvwrapper-win安装配置

如果出现“workon不是内部命令”&#xff0c;先卸载 pip uninstall virtualenvwrapper-win -y 一&#xff0c;指定所有虚拟环境存放路径&#xff1a; # “E:/Python_Envs_List”换成自己想要的路径 setx WORKON_HOME "E:/Python_Envs_List"二&#xff0c;增加pytho…