news 2026/9/10 22:29:11

freeCodeCamp 每日编程挑战实战解析:Challenge 290 “Pizza Party“ 的向上取整与聚合算法

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
freeCodeCamp 每日编程挑战实战解析:Challenge 290 “Pizza Party“ 的向上取整与聚合算法

freeCodeCamp 每日编程挑战实战解析:Challenge 290 "Pizza Party" 的向上取整与聚合算法

【免费下载链接】freeCodeCampfreeCodeCamp.org's open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp

本篇技术指南以 freeCodeCamp 仓库中的 Challenge 290: Pizza Party(每日编程挑战系列之一)为核心,从题目规则、数学建模、逐行代码实现到测试用例验证,完整剖析这道数组聚合类算法题。读者读完将掌握Math.ceil向上取整、Math.max下限约束、Array.reduce聚合等 JavaScript 核心技巧,并了解该挑战在 freeCodeCamp 每日编程挑战体系(challengeType 28)中的真实运行机制。

一、题目总览:为披萨派对计算订购数量

挑战文件位于 curriculum/challenges/english/blocks/daily-coding-challenges-javascript/69f8c998d78ad3171a0713bd.md,归属于daily-coding-challenges-javascript挑战块。题目场景非常贴近生活:

给定一个数组,表示今天每个人的工作时长(小时),请返回为披萨派对需要订购的披萨数量。

要完成这道题,必须严格遵循以下四条业务规则:

  1. 切片换算:将每个人的工作时长除以 3,得到其应得的披萨切片数;
  2. 禁止半片:不能吃半片披萨,因此每个人的切片数需向上取整到最近的整数;
  3. 最低保障:每个人至少分到 2 片;
  4. 整披萨订购:每份披萨含 8 片,总切片数换算成披萨后需向上取整到最近的整数个披萨。

这是一个典型的"分步计算 → 逐项聚合 → 最终取整"的数组处理问题,考察的是对reduceMath.ceilMath.max的组合运用能力。

二、从题目到数学模型

在动手写代码之前,先把文字规则翻译成数学表达式。

设输入数组为hoursWorked(每人工作时长的数组),那么:

第 1 步:单人的切片数

slices_i = max( ceil(hours_i / 3), 2 )
  • ceil(hours_i / 3):实现"除以 3 后向上取整",对应规则 1 和规则 2;
  • max(..., 2):实现"每人至少 2 片"的下限约束,对应规则 3。

第 2 步:总切片数

totalSlices = sum(slices_i) // 对每个人累加

第 3 步:最终披萨数

pizzas = ceil(totalSlices / 8)
  • 每片披萨 8 片、向上取整,对应规则 4。

值得注意的是,Math.max(Math.ceil(hours / 3), 2)这一表达式把"取整"和"下限"两个逻辑合并成了一步:先算出按工时换算的切片数并取整,再与 2 比较取较大值。这样即使某人的工时小于 6 小时(ceil(hours/3) < 2),也能保证其至少获得 2 片。

三、种子代码与解决方案逐行拆解

3.1 初始种子代码

挑战为学习者提供了如下函数骨架(位于原文档的# --seed--部分):

function getPizzasToOrder(hoursWorked) { return hoursWorked; }

学习者需要把return hoursWorked替换为真正的计算逻辑,使函数接收每人工作时长数组、返回订购披萨数。

3.2 官方解决方案

function getPizzasToOrder(hoursWorked) { const totalSlices = hoursWorked.reduce((sum, hours) => { return sum + Math.max(Math.ceil(hours / 3), 2); }, 0); return Math.ceil(totalSlices / 8); }

逐行解读:

代码片段作用
hoursWorked.reduce((sum, hours) => ..., 0)遍历数组,以0为初始值累加每个人的切片数
Math.ceil(hours / 3)每小时 3 片换算并向上取整(规则 1、2)
Math.max(..., 2)与 2 比较,保证每人最少 2 片(规则 3)
Math.ceil(totalSlices / 8)每披萨 8 片,总切片换算后向上取整(规则 4)

整个方案只用一次reduce就完成了所有人的切片数聚合,再通过一次Math.ceil收尾,时间复杂度和空间复杂度均为O(n)(仅需遍历一次数组、无额外存储),是这道题在性能上的最优解。

四、测试用例验证:理解边界与陷阱

原文档的# --hints--部分给出了 5 个断言用例,每个都用assert.equal校验函数输出。下面逐一演算,帮助理解规则之间的交互:

用例 1:getPizzasToOrder([8, 8, 8])2

每人 8 小时:ceil(8/3) = 3,均大于 2,故每人 3 片,共 9 片;ceil(9/8) = 2。✅

用例 2:getPizzasToOrder([10, 9, 8, 2, 2, 6, 10])3

  • 10 小时 →ceil(10/3) = 4
  • 9 小时 →ceil(9/3) = 3
  • 8 小时 →ceil(8/3) = 3
  • 2 小时 →ceil(2/3) = 1,但max(1, 2) = 2
  • 2 小时 → 同上,2 片
  • 6 小时 →ceil(6/3) = 2
  • 10 小时 → 4 片

合计4+3+3+2+2+2+4 = 20片;ceil(20/8) = 3。✅ 这个用例同时覆盖了"最低 2 片保障"和"整披萨取整"两条规则。

用例 3:getPizzasToOrder([1, 2, 3, 4, 5])2

  • 1 小时 →max(ceil(1/3), 2) = 2
  • 2 小时 →max(1, 2) = 2
  • 3 小时 →max(1, 2) = 2
  • 4 小时 →max(ceil(4/3), 2) = max(2, 2) = 2
  • 5 小时 →max(2, 2) = 2

合计 10 片;ceil(10/8) = 2。✅ 该用例集中考验"工时不足 3 小时也要 2 片"的边界逻辑。

用例 4:getPizzasToOrder([8, 8, 8, 8, 8, 8, 8, 8])3

8 人各 3 片,共 24 片;ceil(24/8) = 3。✅ 恰好整批 3 个披萨,验证无余数时的取整行为。

用例 5:getPizzasToOrder([9, 9, 6])1

  • 9 小时 →ceil(9/3) = 3(两次)
  • 6 小时 →ceil(6/3) = 2

合计 8 片;ceil(8/8) = 1。✅ 恰好 1 个披萨,再次验证"整除"场景。

用例 6:getPizzasToOrder([10, 12, 16, 9, 8, 11, 15, 8, 0])5

  • 10 → 4、12 → 4、16 →ceil(16/3)=6、9 → 3、8 → 3、11 →ceil(11/3)=4、15 → 5、8 → 3、0 →max(0, 2) = 2

合计4+4+6+3+3+4+5+3+2 = 34片;ceil(34/8) = 5。✅ 该用例包含 0 工时(考验最低切片保障)和较大数值的聚合。

从这 6 组用例可以看出测试设计覆盖了:一般换算、下限约束、整除与不整除、0 工时等典型边界情况。

五、挑战在 freeCodeCamp 体系中的定位

5.1 挑战块与元数据

本挑战的 frontmatter 声明了challengeType: 28dashedName: challenge-290,并在 curriculum/structure/blocks/daily-coding-challenges-javascript.json 中注册为"Challenge 290: Pizza Party"(id 为69f8c998d78ad3171a0713bd),前后分别是 Challenge 289 "Sum of Differences" 与 Challenge 291 "FizzBuzz Count"。

从该块配置可看出其工程特征:

  • "isUpcomingChange": true:属于进行中的课程变更;
  • "usesMultifileEditor": true:在挑战编辑界面使用多文件编辑器;
  • "helpCategory": "JavaScript":归入 JavaScript 帮助分类;
  • "blockLayout": "legacy-challenge-list":采用传统挑战列表布局。

5.2 每日挑战的 API 支撑

此类挑战的数据由 API 侧的 daily-coding-challenge 模块对外提供。路由实现位于 api/src/daily-coding-challenge/routes/daily-coding-challenge.ts,共注册了 6 个公开 GET 端点:

端点说明
/daily-coding-challenge/date/:dateYYYY-MM-DD精确日期获取某天挑战
/daily-coding-challenge/day/:dayMM-DD获取对应日期的挑战(2 月 29 日映射到 2 月 28 日)
/daily-coding-challenge/today获取美国中部时区(America/Chicago)当天的挑战
/daily-coding-challenge/month/:monthYYYY-MM获取整月挑战列表
/daily-coding-challenge/all获取全部挑战列表(仅返回已发布日期)
/daily-coding-challenge/newest获取最新一期挑战的日期

请求/响应结构由 TypeBox schema 定义在 api/src/daily-coding-challenge/schemas/daily-coding-challenge.ts:单个挑战响应包含iddatechallengeNumbertitledescription以及javascript/python两套语言字段(各含testschallengeFiles)。Pizza Party 这类题目的测试断言正是在此类结构中以testString形式交付给前端执行的。

5.3 日期相关的实现细节

源码中有几个值得注意的日期处理细节(见 api/src/daily-coding-challenge/utils/helpers.ts 与路由文件):

  • "今天"以美国中部时间为准,由getNowUsCentral()计算,再经getUtcMidnight归一化为 UTC 零点用于数据库查询;
  • getSourceDate会把MM-DD映射回具体的 UTC 日期,并处理闰年 2 月 29 日这类边界;
  • 所有查询都会过滤date <= today US Central的挑战,避免返回未发布的未来题目(路由注释明确指出最后一个挑战日期为 2026 年 8 月 10 日之后将不再返回数据)。

六、举一反三:从本题延伸的实战要点

6.1 取整三件套的选择

  • Math.ceil(x):向上取整,适合"必须准备足量"的场景(本题的披萨、物资采购、服务器容量规划);
  • Math.floor(x):向下取整,适合"最多能装多少"的场景;
  • Math.round(x):四舍五入,适合统计平均类场景。

6.2 用Math.max施加下限约束

Math.max(value, min)是给计算结果设置下限的惯用写法,等价于value < min ? min : value。同理,Math.min(value, max)可以施加上限。本题的"每人至少 2 片"正是这种模式的典型应用。

6.3reduce的聚合思维

当需要把数组中的每一项折算成一个数值时,reduce(callback, initialValue)是最直接的工具。本题把"每人切片数"在回调内即时计算并累加,避免先mapsum的两趟遍历,代码更紧凑。若希望可读性优先,也可拆成两步:

function getPizzasToOrder(hoursWorked) { const slicesPerPerson = hoursWorked.map(hours => Math.max(Math.ceil(hours / 3), 2) ); const totalSlices = slicesPerPerson.reduce((sum, s) => sum + s, 0); return Math.ceil(totalSlices / 8); }

两种写法结果一致,前者更高效,后者更利于调试中间值。

6.4 可能的陷阱

  • 忘记最低切片保障:若直接sum += Math.ceil(hours / 3),则[1, 2]这类用例会得到ceil(1/3)+ceil(2/3) = 2片、1 个披萨,而正确答案要求每人 2 片,即 4 片、1 个披萨——对于本例恰好一致,但在更大的输入下会显著低估订购量;
  • 先取整再求和 vs 先求和再取整:必须"每人分别向上取整后再累加",而不是"总工时除以 3 后一次性取整",否则会系统性少算切片;
  • 忽略 0 工时成员ceil(0/3) = 0,若不做max(..., 2)约束,0 工时者将得不到任何切片。

七、总结

Challenge 290: Pizza Party 是一道短小精悍的数组聚合题:通过"工时 ÷ 3 向上取整、每人至少 2 片、每 8 片 1 个披萨向上取整"三层规则,串起了Math.ceilMath.maxArray.prototype.reduce三个高频 API,并用 6 组覆盖边界条件的断言固化了正确行为。学习者既可以把它当作日常算法练习,也可以顺着本仓库的 challenge 文档、挑战块配置 与 API 路由实现 三条线索,完整理解一道每日编程挑战从题目定义、数据提供到在线判题的全链路工程实现。

【免费下载链接】freeCodeCampfreeCodeCamp.org's open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 22:27:57

留个神!不是每款 AI 都能用来写学术论文,2026 高校认可工具精选

每年毕业季&#xff0c;无数同学深陷论文难题&#xff1a;开题毫无思路、搭建框架耗费数日、初稿逻辑松散、查重标红泛滥、AI检测超标、格式反复被导师驳回。面对繁重的写作任务&#xff0c;许多学生开始依赖通用型AI工具&#xff0c;但市面上大多数AI平台存在严重短板。它们往…

作者头像 李华
网站建设 2026/9/10 22:27:36

SpringBoot打印店预约系统:技术实现与优化

1. 项目背景与核心价值打印店作为高校和办公区的高频服务场所&#xff0c;传统的人工登记模式存在三大痛点&#xff1a;高峰期排队耗时、订单状态不透明、文件安全管理薄弱。这套基于SpringBoot的预约取件系统&#xff0c;正是为解决这些实际问题而设计的轻量级解决方案。我在实…

作者头像 李华
网站建设 2026/9/10 22:27:22

Python与Milvus构建高效向量搜索系统指南

1. 为什么选择PythonMilvus这个技术组合&#xff1f;Milvus作为一款开源的向量数据库&#xff0c;在处理非结构化数据时展现出独特优势。而Python凭借其简洁语法和丰富生态&#xff0c;成为AI领域事实上的标准语言。这两者的结合&#xff0c;为开发者提供了从数据预处理到向量存…

作者头像 李华
网站建设 2026/9/10 22:26:19

无线传感器网络LEACH协议优化与DBN能效提升方案

1. 项目概述&#xff1a;无线传感器网络中的智能优化挑战在物联网和工业4.0时代&#xff0c;无线传感器网络(WSN)作为物理世界与数字世界的桥梁&#xff0c;其能效和成本优化一直是研究热点。传统LEACH协议虽然解决了集群头选择的基本问题&#xff0c;但在动态环境和复杂应用场…

作者头像 李华
网站建设 2026/9/10 22:25:25

实验动物预约订购系统:数字化管理解决方案

1. 实验动物预约订购系统概述实验室动物管理一直是科研机构面临的重要挑战。传统的人工登记方式效率低下&#xff0c;容易出错&#xff0c;特别是在多课题组共用动物房的情况下。我们团队开发的这套实验动物预约订购系统&#xff0c;正是为了解决这些痛点而生。这个系统本质上是…

作者头像 李华
网站建设 2026/9/10 22:24:38

Sourcetrail 源码可视化上手指南:30分钟读通一个陌生 C++ 仓库

Sourcetrail 源码可视化上手指南&#xff1a;30分钟读通一个陌生 C 仓库 【免费下载链接】Sourcetrail Sourcetrail - free and open-source interactive source explorer 项目地址: https://gitcode.com/GitHub_Trending/so/Sourcetrail Sourcetrail 是一款开源、跨平台…

作者头像 李华