当前位置: 首页 > news >正文

糖果大冒险:公平分发的智慧挑战

🌟《糖果分发大冒险:智慧守护者的挑战》


在一个遥远而美丽的小镇上,住着一群可爱的孩子。每年一度的“甜蜜节”即将到来,镇长决定为每个孩子分发糖果。但是,为了鼓励孩子们努力学习和表现自己,镇长制定了一个特别的规则:

评分高的孩子必须比相邻的孩子获得更多糖果

作为镇上的智慧守护者——你,被委以重任,需要设计一种公平且高效的糖果分配方案,确保每个孩子都能得到应得的糖果,并且总数尽可能少。


📖 故事背景

假设现在有 n 个孩子站成一排,每个孩子都有一个评分 ratings[i],表示他们在过去一年中的综合表现。你的任务是根据这些评分,给每个孩子分发最少数量的糖果,同时满足以下条件:

  1. 每个孩子至少有一颗糖果。

  2. 如果某个孩子的评分比相邻的孩子高,则该孩子必须获得更多的糖果。

🧠 解题思路:两次遍历法

核心思想

我们可以将问题拆解为两个单向规则来简化处理:

  • 左规则:从左到右遍历,如果当前孩子的评分比左边的孩子高,则他应该比左边的孩子多一颗糖果。

  • 右规则:从右到左遍历,如果当前孩子的评分比右边的孩子高,则他应该比右边的孩子多一颗糖果。

最终,每个孩子的糖果数应该是这两个规则中较大值的组合。

实现步骤

  1. 初始化

    • 创建一个数组 left,用于存储每个孩子根据左规则所需的糖果数。

    • 使用一个变量 right 动态记录右规则下的糖果数。

  2. 左规则遍历

    • 从左到右遍历评分数组,如果当前孩子的评分高于前一个孩子,则给他比前一个多一颗糖果;否则,给他一颗糖果(最小值)。

  3. 右规则遍历

    • 从右到左遍历评分数组,维护一个递增链 right。如果当前孩子的评分高于右边的孩子,则增加 right 值;否则,重置 right 为 1。

    • 对于每个孩子,计算其最终糖果数为 max(left[i], right)

  4. 累加求和

    • 将所有孩子的最终糖果数相加,得到最少需要准备的糖果总数。


💻 完整代码实现

#include <iostream>#include <vector>#include <algorithm>using namespace std;​/*** @brief 计算最少需要准备的糖果总数* @param ratings 孩子们的评分数组* @return 最少糖果总数*/int candy(vector<int>& ratings) {int n = ratings.size();// Step 1: 初始化 left 数组,每个孩子至少一颗糖果vector<int> left(n, 1);// Step 2: 左规则遍历,从左到右for (int i = 1; i < n; ++i) {if (ratings[i] > ratings[i - 1]) {left[i] = left[i - 1] + 1;}}​// Step 3: 右规则遍历,从右到左int right = 1;int totalCandies = 0;for (int i = n - 1; i >= 0; --i) {if (i < n - 1 && ratings[i] > ratings[i + 1]) {// 当前孩子的评分比右边高,递增链继续right++;} else {// 递增链断开,重置 right 为 1right = 1;}// 最终糖果数取左右规则的最大值totalCandies += max(left[i], right);}​return totalCandies;}​// 测试主函数int main() {// 示例输入vector<int> ratings = {1, 0, 2};cout << "孩子们的评分: ";for (int rating : ratings) {cout << rating << " ";}cout << endl;​int result = candy(ratings);cout << "最少需要准备的糖果总数: " << result << endl;  // 输出: 5​return 0;}

📊 复杂度分析

项目时间复杂度空间复杂度
整体O(n) —— 两次遍历数组O(n) —— 需要额外的 left 数组

🎯 关键点解析

1. 左规则遍历

在第一次遍历时,我们只考虑每个孩子相对于左边的情况:

  • 如果 ratings[i] > ratings[i-1],则 left[i] = left[i-1] + 1

  • 否则,left[i] = 1(每个孩子至少有一颗糖果)。

这一步保证了每个孩子在其左侧邻居评分低于自己的情况下,能够获得足够的糖果。

2. 右规则遍历

在第二次遍历时,我们从右向左遍历数组,动态维护一个递增链 right

  • 如果 ratings[i] > ratings[i+1],则递增链继续,right++

  • 否则,递增链断开,right = 1

这一步确保了每个孩子在其右侧邻居评分低于自己的情况下,也能获得足够的糖果。

3. 最终糖果数

对于每个孩子,其最终糖果数为 max(left[i], right),这样可以同时满足左右两个方向的要求。


🎨 图解示例

假设 ratings = [1, 0, 2]

左规则遍历结果:

索引评分左规则糖果数
011
101
222

右规则遍历过程:

  • 从右向左

    • i=2: right = 1

    • i=1: ratings[1] < ratings[2]right = 2

    • i=0: ratings[0] > ratings[1]right = 1

最终糖果数:

索引评分左规则糖果数右规则糖果数最终糖果数
01111
10122
22212

总糖果数 = 1 + 2 + 2 = 5


🚨 防止越界的具体实现

为了更清晰地理解如何防止越界,让我们再看一个极端的例子:

假设 ratings = [5],只有一个孩子:

1. 初始化
 vector<int> left(1, 1);  // 左规则数组,初始值为1int right = 1;           // 右规则变量,初始值为1int totalCandies = 0;    // 总糖果数
2. 第一次遍历(从左到右)
索引评分左规则糖果数
051
3. 第二次遍历(从右到左)
  • i = 0:

    • i < n - 1 不成立(因为 i == 0),直接进入 else 分支。

    • right = 1

    • totalCandies += max(left[0], right) = max(1, 1) = 1

4. 总糖果数
totalCandies = 1

📝 关键总结

  1. 防止越界的关键在于条件 i < n - 1

    • 这个条件确保了只有当 i 不是最后一个元素时,才会进行 ratings[i + 1] 的比较。

    • 如果 i == n - 1,则直接进入 else 分支,避免访问越界的 ratings[i + 1]

  2. 对于最后一个元素

    • 最后一个元素没有右边的孩子,因此我们不需要比较它的评分与右边的孩子。

    • 直接将 right 设为 1,表示该孩子至少有一颗糖果。

通过这种方式,我们能够安全地处理数组的边界情况,确保算法的正确性和稳定性。


🎯 总结

主要收获

  1. 分解问题:将复杂的双向约束分解为两个单向约束,分别处理。

  2. 贪心策略:每次尽量给出最小的满足条件的糖果数,最后合并两个方向的结果。

  3. 优化空间:虽然这里用了额外的 left 数组,但也可以通过一些技巧进一步优化空间复杂度。

具体实现步骤

  1. 初始化:创建 left 数组和 right 变量。

  2. 左规则遍历:从左到右遍历,更新 left 数组。

  3. 右规则遍历:从右到左遍历,动态维护 right,并计算最终糖果数。

  4. 累加求和:计算总糖果数。

代码实现

 #include <iostream>#include <vector>#include <algorithm>using namespace std;​int candy(vector<int>& ratings) {int n = ratings.size();// Step 1: 初始化 left 数组,每个孩子至少一颗糖果vector<int> left(n, 1);// Step 2: 左规则遍历,从左到右for (int i = 1; i < n; ++i) {if (ratings[i] > ratings[i - 1]) {left[i] = left[i - 1] + 1;}}​// Step 3: 右规则遍历,从右到左int right = 1;int totalCandies = 0;for (int i = n - 1; i >= 0; --i) {if (i < n - 1 && ratings[i] > ratings[i + 1]) {// 当前孩子的评分比右边高,递增链继续right++;} else {// 递增链断开,重置 right 为 1right = 1;}// 最终糖果数取左右规则的最大值totalCandies += max(left[i], right);}​return totalCandies;}​// 测试主函数int main() {vector<int> ratings = {1, 0, 2};cout << "孩子们的评分: ";for (int rating : ratings) {cout << rating << " ";}cout << endl;​int result = candy(ratings);cout << "最少需要准备的糖果总数: " << result << endl;  // 输出: 5​return 0;}

希望这个故事能帮助你更好地理解并掌握「分发糖果」问题的核心思想!如果你有任何疑问或需要进一步的帮助,请随时告诉我! 😊

http://www.dtcms.com/a/325030.html

相关文章:

  • Stagewise使用指南:从项目集成到效能跃迁的深度解析
  • 【算法题】:和为N的连续正数序列
  • AI大模型-提示词工程
  • 01 词法分析陷阱:C编程中的符号误解
  • 深度解析 Spring Boot 循环依赖:原理、源码与解决方案
  • PhotoDirector 安卓版:功能强大的照片编辑与美化应用
  • TypeScript中的type和interface的区别是什么?
  • Shell脚本-数组定义
  • OpenEnler等Linux系统中安装git工具的方法
  • DDR中的POD与ODT
  • 分布式事务原理(高并发系统下的数据一致性保障)
  • 一、线性规划
  • 免费数字人API开发方案
  • 高精度计算+快速幂算法
  • 【算法题】:斐波那契数列
  • 【langchain】如何给langchain提issue和提pull request?
  • SpringIoc 实践和应用--XML配置
  • 数据结构-deque(双端队列)和queue(队列)区别
  • Flask多进程数据库访问问题详解
  • spring全家桶使用教程
  • Lua语言元表、协同程序
  • 密码学的数学基础2-Paillier为什么产生密钥对比RSA慢
  • SQL三剑客:DELETE、TRUNCATE、DROP全解析
  • 深度相机---双目深度相机
  • 浏览器CEFSharp+X86+win7 之 浏览器右键菜单(六)
  • Mysql笔记-存储过程与存储函数
  • vulnhub-doubletrouble靶场攻略
  • Linux C文件操作函数
  • 谷歌DeepMind发布Genie 3:通用型世界模型,可生成前所未有多样化的交互式虚拟环境
  • C++移动语义、完美转发及编译器优化零拷贝