洛谷 B4450 / B3867 / B3923 智慧购物、储蓄与做题------从程序到生活
📌 摘要
B4450 用数组追踪每种文具的最低价,B3867 用数组累加每个储蓄罐的存款,B3923 用三变量滚动模拟斐波那契式做题计划并设终止条件。三道题都以"小杨的日常生活"为背景------购物、存钱、学习------恰好覆盖了最常见的三类应用需求:比价、记账、计划 。三道题共享一个数据结构------数组 ------分别用作"最小值追踪器"、"累加器"和"滚动窗口"。本文从伪代码题解出发,延伸到这些算法模式如何变成真实应用:比价网站(淘宝/京东的最低价筛选)、记账软件(支付宝/微信账单)、习惯养成 App------你在洛谷上写的 a[k]=p 和 ans[a]+=i,在工业界是比价引擎和个人金融系统的核心逻辑。
题目链接 :B4450 小杨的智慧购物 | B3867 小杨的储蓄 | B3923 小杨做题
📚 目录
-
[📝 前言](#📝 前言)
-
[🔍 三道题在考什么](#🔍 三道题在考什么)
-
[🛒 B4450:小杨的智慧购物](#🛒 B4450:小杨的智慧购物)
-
[💡 思路](#💡 思路)
-
[📝 伪代码](#📝 伪代码)
-
[🎯 关键点](#🎯 关键点)
-
-
[💰 B3867:小杨的储蓄](#💰 B3867:小杨的储蓄)
-
[💡 思路](#💡 思路)
-
[📝 伪代码](#📝 伪代码)
-
[🎯 关键点](#🎯 关键点)
-
-
[📚 B3923:小杨做题](#📚 B3923:小杨做题)
-
[💡 思路](#💡 思路)
-
[📝 伪代码](#📝 伪代码)
-
[🎯 关键点](#🎯 关键点)
-
-
[⚖️ 三题对比](#⚖️ 三题对比)
-
[⚠️ 注意事项](#⚠️ 注意事项)
-
[🌳 延伸:从程序到生活------算法模式如何变成应用](#🌳 延伸:从程序到生活——算法模式如何变成应用)
-
[🛒 比价与最低价追踪](#🛒 比价与最低价追踪)
-
[💰 累加器模式:记账与储蓄](#💰 累加器模式:记账与储蓄)
-
[📈 斐波那契式增长:习惯与复利](#📈 斐波那契式增长:习惯与复利)
-
[📊 数组------从考试题到数据库](#📊 数组——从考试题到数据库)
-
[🏠 三道题的现实映射](#🏠 三道题的现实映射)
-
-
[📚 延伸阅读文献](#📚 延伸阅读文献)
📝 前言
这篇题解没有源代码,只有伪代码。
作为一名信奥教练,我不提倡复制粘贴。我见过太多学生搜到题解、复制、粘贴、提交、AC------代码跑通了,脑子没跑通。下次遇到变体题,还是不会。
伪代码剥掉了语言的壳,只留算法的骨架。你看不到 #include,看不到 cin、cout,看不到那些让你以为"我会了"的语法细节。你能看到的只有:这一步做什么、下一步做什么、为什么这么做。
如果你是路过的友友,已经在这道题上挣扎了很久------先去喝杯水,回来重新看看自己卡在哪一步。是没读懂题意?是思路方向偏了?还是代码有 bug 但逻辑其实对?大多数时候不是不会,是走偏了。偏了不可怕,可怕的是偏了之后直接放弃,去抄一份能 AC 的代码。抄完你以为你懂了,其实你只是搬了别人的结论。
除非你时间真的紧张------比赛临近、作业要交------那种情况先 AC 再说,能理解。但平时练习,给自己一点耐心。先自己想、自己写、自己调,跑不过了再来看伪代码:你的思路和这里差在哪一步。那一步,就是你真正学到的东西。
🔍 三道题在考什么
三道题都以"小杨的日常生活"为背景,但考察的算法模式截然不同:
| B4450 智慧购物 | B3867 储蓄 | B3923 做题 | |
|---|---|---|---|
| 生活场景 | 买文具找最低价 | 每天往储蓄罐存钱 | 按斐波那契式计划做题 |
| 数组用途 | 最小值追踪器 | 累加器 | ---(三变量滚动) |
| 核心操作 | a[k]=min(a[k],p) |
ans[a]+=i |
a,b,sum 三变量递推 |
| 算法模式 | 分组取最小 | 分组累加 | 递推 + 提前终止 |
| 现实对应 | 比价网站 | 记账软件 | 习惯养成 App |
B4450 用数组下标当"种类编号",数组值存该种类的最低价------每来一件新商品,比较并更新。
B3867 用数组下标当"储蓄罐编号",数组值存该罐的总金额------每来一天,把天号(等于金额)累加进去。
B3923 不用数组,用三个变量滚动模拟斐波那契式序列,到达阈值就停。
三道题对应了三种最基本的程序模式:选择(取最小)、累积(加起来)、递推(用前项算后项)。这三种模式覆盖了日常生活中 80% 的计算需求。
🛒 B4450:小杨的智慧购物
💡 B4450 思路
M 种文具,N 件商品。对每件商品(种类 k,价格 p),更新种类 k 的最低价。最后把 M 个种类的最低价加起来。
数组 a[k] 用种类编号 k 做下标,存该种类目前的最低价。初始化为 0(表示还没见过该种类)。读入一件商品时:如果 a[k]==0(第一次见),设为 p;如果 a[k]>p(更便宜),更新为 p。
📝 B4450 伪代码
text
读取 M, N
数组 a[1..M] 初始化为 0 // a[k] = 种类 k 的最低价
对 i = 1 到 N:
读取 k, p
如果 a[k] == 0: // 第一次见到这个种类
a[k] = p
如果 a[k] > p: // 比已知的更便宜
a[k] = p
总价 = 0
对 k = 1 到 M:
总价 += a[k] // 每种种类的最低价求和
输出 总价
🎯 B4450 关键点
数组下标 = 种类编号。 这是数组最强大的用法之一------用下标直接索引到对应类别,O(1) 查找和更新。不需要遍历搜索。
a[k]==0 的"首次"判断。 初始值为 0 表示"未见过"。第一次遇到种类 k 时 a[k]==0,设为 p。之后只做 a[k]>p 的比较。因为价格 ≥1,0 永远不会被误认为有效价格。
用样例追踪:
| 输入 | a1 | a2 | 说明 |
|---|---|---|---|
| 1 1 | 1 | 0 | 种类1首次,价格1 |
| 1 2 | 1 | 0 | 2>1,不更新 |
| 1 1 | 1 | 0 | 1不<1,不更新 |
| 2 3 | 1 | 3 | 种类2首次,价格3 |
| 2 10 | 1 | 3 | 10>3,不更新 |
总价 = a1+a2 = 1+3 = 4。
"分组取最小"模式。 这道题的本质是:把 N 件商品按种类分组,每组取最小值,再求和。在数据处理中这是 GROUP BY + MIN + SUM 的模式------SQL 里的一行聚合查询。
💰 B3867:小杨的储蓄
💡 B3867 思路
N 个储蓄罐编号 0 到 N-1。第 i 天往储蓄罐 a_i 里存 i 元。D 天后输出每个储蓄罐的总金额。
数组 ans[a] 用储蓄罐编号做下标,每来一天把天号 i(等于存入金额)累加进去。
📝 B3867 伪代码
text
读取 N, D
数组 ans[0..N-1] 初始化为 0 // ans[j] = 储蓄罐 j 的总金额
对 i = 1 到 D: // 第 i 天
读取 a // 今天选的储蓄罐编号
ans[a] += i // 存入 i 元(天号 = 金额)
对 j = 0 到 N-1:
输出 ans[j],后跟空格
🎯 B3867 关键点
累加器模式。 ans[a] += i 一行代码完成了"找到对应储蓄罐"和"累加金额"两个操作。数组下标直接定位储蓄罐,不需要搜索。
用样例 1 追踪(N=2, D=3, 选择序列 0,1,0):
| 天 i | 储蓄罐 a | 操作 | ans0 | ans1 |
|---|---|---|---|---|
| 1 | 0 | ans0 += 1 | 1 | 0 |
| 2 | 1 | ans1 += 2 | 1 | 2 |
| 3 | 0 | ans0 += 3 | 4 | 2 |
输出:4 2。
天号 = 金额。 第 i 天存 i 元------题目把"天数"和"金额"巧妙地绑定在一起。ans[a] += i 中的 i 既是循环计数器又是存入金额,一行代码两个用途。
0 号编号。 储蓄罐从 0 开始编号,不是 1。输出也从 0 号开始。这是 C/C++ 的习惯------数组下标从 0 开始。
📚 B3923:小杨做题
💡 B3923 思路
第 1 天做 a 题,第 2 天做 b 题,第 3 天起每天 = 前两天之和(斐波那契式)。如果某天做了 ≥ m 题,之后不再做题。求 N 天内总共做了多少题。
用三个变量 a(前前天)、b(前天)、sum(今天)滚动递推。每天做完后检查 b >= m 是否满足终止条件。
📝 B3923 伪代码
text
读取 a, b, m, N
total = 0
sum = 0
对 i = 1 到 N:
如果 i == 1:
sum = a // 第1天
total += a
否则如果 i == 2:
sum = sum + b // 第2天,sum 此时 = a + b
total += b
否则: // 第3天起
a = b // 滚动:前前天 = 原前天
b = sum // 滚动:前天 = 原今天
sum = a + b // 今天 = 前前天 + 前天
total += b // 累加今天做的题数
如果 b >= m: // 达到阈值,停止
跳出循环
输出 total
🎯 B3923 关键点
三变量滚动。 斐波那契式序列只需要前两项,不需要存整个序列。用 a、b、sum 三个变量滚动------每次迭代把 b 传给 a、sum 传给 b、新值传给 sum。空间 O(1)。
用样例 1 追踪(a=1, b=2, m=10, N=5):
| 天 i | a(前前) | b(前) | sum(今) | 做的题 | total | b≥m? |
|---|---|---|---|---|---|---|
| 1 | 1 | 2 | 1 | 1 | 1 | 2<10 |
| 2 | 1 | 2 | 3 | 2 | 3 | 2<10 |
| 3 | 2 | 3 | 5 | 3 | 6 | 3<10 |
| 4 | 3 | 5 | 8 | 5 | 11 | 5<10 |
| 5 | 5 | 8 | 13 | 8 | 19 | 8<10 |
输出:19。序列 1, 2, 3, 5, 8 是斐波那契式(每项 = 前两项之和)。
提前终止。 样例 2(a=1, b=1, m=5, N=8):第 5 天做了 5 题 ≥ m=5,break。total=1+1+2+3+5=12 。N=8 但只做了 5 天------break 跳出循环,后面的天不做也不加。
"b >= m"的检查时机。 检查在每天做完之后------当天做到 ≥m 题仍然算入 total,但从第二天起不做题。如果题目改成"做到 ≥m 题当天就不算",检查要移到累加之前。
🔧 两个建议修复的隐患。 原始代码有两个依赖"碰巧"才正确的写法,建议显式修复:
| 隐患 | 原始代码 | 问题 | 建议修复 |
|---|---|---|---|
sum 未初始化 |
int sum; |
局部变量不初始化,值未定义。碰巧为 0 才正确 | int sum = 0; |
第 1 天用 += |
sum += a; |
依赖 sum 初始为 0。如果 sum 有垃圾值,结果错 |
sum = a;(设值,不是累加) |
修复后的第 1-2 天逻辑:
text
如果 i == 1:
sum = a // 设值,不是累加
total += a
否则如果 i == 2:
sum = a + b // 直接算 a+b,不依赖上一步
total += b
为什么原代码"碰巧"能过? 因为大多数编译器在非优化模式下把未初始化的局部变量零初始化(sum 碰巧为 0),所以 sum += a 等价于 sum = 0 + a = a。但这是未定义行为 ------C++ 标准不保证未初始化变量为 0。换一个编译器或开优化,可能就错了。考试能过不代表代码正确。
⚖️ 三题对比
| B4450 智慧购物 | B3867 储蓄 | B3923 做题 | |
|---|---|---|---|
| 数组用途 | 最小值追踪 | 累加器 | 不用数组(三变量滚动) |
| 核心操作 | a[k] = min(a[k], p) |
ans[a] += i |
sum = a + b 滚动 |
| 算法模式 | 分组取最小 | 分组累加 | 递推 + 提前终止 |
| 数组大小 | O(M) | O(N) | O(1) |
| 时间复杂度 | O(N) | O(D) | O(min(N, 终止天)) |
| 生活场景 | 购物比价 | 存钱记账 | 学习计划 |
| 现实对应 | 比价网站 | 记账软件 | 习惯养成 App |
B4450 和 B3867 都用数组做"分组"------一个按下标分组取最小,一个按下标分组累加。B3923 不需要分组,只需要递推。三种模式覆盖了日常计算的三种基本需求:选择最优、累积总量、预测趋势。
⚠️ 注意事项
-
B4450 的
a[k]==0判断 :用 0 表示"未见过",因为价格 ≥1。如果价格可能为 0,需要换一个哨兵值(如 -1 或INT_MAX)。 -
B4450 的
long long:M≤10⁵,每个最低价≤10³,总价最多 10⁸------int能存(上限约 2×10⁹),但用long long更安全。 -
B3867 的 0 号编号:储蓄罐从 0 开始,输出也从 0 号开始。如果习惯从 1 开始编号,容易搞错。
-
B3923 的
sum未初始化 :原始代码中sum声明后未赋初值,依赖编译器零初始化。虽然实际能过,但这是未定义行为 ------建议显式初始化sum = 0。 -
B3923 的
sum += avssum = a:第一行用+=而非=,依赖sum初始为 0。如果sum有垃圾值,结果就错了。第一行应该是sum = a(设值),不是sum += a(累加)。 -
B3923 的终止条件位置 :
if(b >= m) break在循环体末尾------当天做的题仍然算入 total,从下一天起停止。如果题意是"当天达到 m 就不算",需要调整位置。
🌳 延伸:从程序到生活------算法模式如何变成应用
你说这三道题"从程序到生活,到贴近生活,让日常更为便捷的应用需求的出现"。没错------三道题的算法模式分别对应了三种最常见的日常应用:比价、记账、计划。你在洛谷上写的几行代码,在工业界是真实产品的核心逻辑。
🛒 比价与最低价追踪
B4450 的 a[k] = min(a[k], p) 做的事是:对每个商品类别,追踪最低价。这正是比价网站的核心算法。
| 应用 | 做什么 | 和 B4450 的对应 |
|---|---|---|
| 淘宝/京东"价格筛选" | 用户选"价格从低到高",对每类商品显示最低价 | a[k] = min(a[k], p) |
| 什么值得买 | 跨平台比价,追踪历史最低价 | a[k] 存历史最低,新价格来了比较 |
| Google Shopping | 跨商家比价,按类别分组 | GROUP BY 类别 + MIN(价格) |
| 航旅纵横/携程"最低票价" | 对每条航线追踪最低票价 | a[航线] = min(a[航线], 当前票价) |
B4450 的数据规模是 N=10⁵,O(N) 一趟扫完。真实比价网站的数据规模是 N=10⁸(全国商品),用同样的算法但加了分布式存储和缓存。算法不变,规模变了一千倍。
比价网站的技术栈:爬虫抓取商品数据 → 按类别分组存入数据库(a[k] 变成了数据库表的一行)→ 查询时取每类最低价(SELECT MIN(price) FROM products GROUP BY category)→ 前端展示。你在 B4450 里用一个 for 循环做的事,比价网站用一条 SQL 查询做同样的事。
💰 累加器模式:记账与储蓄
B3867 的 ans[a] += i 做的事是:对每个储蓄罐,累加每次存入的金额。这是记账软件的核心模式。
| 应用 | 做什么 | 和 B3867 的对应 |
|---|---|---|
| 支付宝/微信账单 | 按类别(餐饮/交通/购物)累加支出 | ans[类别] += 金额 |
| 银行系统 | 按账户累加存取款 | ans[账户号] += 交易额 |
| 家庭记账 App | 按成员/类别/月份分组累加 | ans[维度] += 金额 |
| 健身 App | 按日期/运动类型累加卡路里 | ans[日期] += 消耗量 |
B3867 中"天号 = 金额"的设定,在记账软件里变成了"时间戳 = 交易时间,金额 = 交易额"------两者解耦了,但累加逻辑一样:account[target] += amount。
记账软件的技术栈:用户记录每笔交易 → 按维度(类别/日期/账户)分组累加 → 生成报表。你在 B3867 里用 ans[a] += i 一行做的事,记账软件用数据库的 SUM(amount) GROUP BY category 做同样的事。
📈 斐波那契式增长:习惯与复利
B3923 的做题计划是斐波那契式增长------每天做的题数是前两天之和。这种"用前项推后项"的模式在生活规划中无处不在。
| 应用 | 做什么 | 和 B3923 的对应 |
|---|---|---|
| 习惯养成 App(如 Streaks) | 逐日递增的习惯强度,达到目标后维持 | 递推 + 终止条件 |
| 学习计划工具 | 每天复习量按遗忘曲线递推 | 艾宾浩斯曲线(类似递推) |
| 理财复利计算 | 每年收益 = 本金 × (1+利率),递推 N 年 | total = total * (1+r) |
| 健身计划(渐进超负荷) | 每周重量 = 上周 + 增量 | 递推式增长 |
B3923 的斐波那契式增长在现实中不常见(没人真能每天做前一天 + 前前天量的题),但"递推 + 终止"的模式是所有计划类应用的底层逻辑:从初始状态出发,按规则推进,达到阈值就停。
番茄钟、背单词 App、健身记录器------都在做同样的事:一个循环,每步更新状态,检查终止条件。你在 B3923 里写的 for + break,在习惯养成 App 里是 while (未达成目标) + 更新进度。
📊 数组------从考试题到数据库
三道题中 B4450 和 B3867 都用数组做"分组"。这不是巧合------数组是所有数据处理的基础结构。
| 洛谷题 | 数组下标 | 数组值 | 数据库对应 |
|---|---|---|---|
| B4450 | 种类编号 | 最低价 | SELECT MIN(price) FROM products GROUP BY category |
| B3867 | 储蓄罐编号 | 总金额 | SELECT SUM(amount) FROM transactions GROUP BY account |
| B3923 | ---(三变量) | --- | 不需要数据库,单行递推 |
数组下标 = 数据库的 GROUP BY 键。数组值 = 数据库的聚合结果(MIN/SUM)。你在洛谷上用 a[k] 做的事,数据库用 GROUP BY 做------只是数据库帮你封装了循环和查找。
| 数组操作 | C++ 代码 | SQL 对应 |
|---|---|---|
| 分组取最小 | if(a[k]>p) a[k]=p |
MIN(price) ... GROUP BY k |
| 分组累加 | ans[a] += i |
SUM(amount) ... GROUP BY a |
| 遍历求和 | sum += a[i] |
SUM(a[i]) |
数组的本质是什么? 是一个"按下标直接定位"的存储结构。数据库的索引做了同样的事------用 B+ 树把"按键查找"从 O(N) 优化到 O(log N)。你在 B4450 里用 a[k] 直接定位种类 k 的最低价,数据库用索引直接定位某个键的记录------思路一样,实现不同。
🏠 三道题的现实映射
把三道题和延伸放在一起:
| 洛谷题 | 算法模式 | 生活场景 | 真实应用 | 技术栈 |
|---|---|---|---|---|
| B4450 | 分组取最小 | 购物比价 | 淘宝/什么值得买 | 爬虫 + 数据库 + SQL GROUP BY |
| B3867 | 分组累加 | 存钱记账 | 支付宝账单/银行系统 | 交易流水 + SUM GROUP BY |
| B3923 | 递推 + 终止 | 学习计划 | 习惯养成 App/番茄钟 | 状态机 + while 循环 |
三道题覆盖了日常应用的三种核心计算需求:选择最优(比价)、累积总量(记账)、预测趋势(计划) 。你在洛谷上写的 a[k]=min(a[k],p)、ans[a]+=i、sum=a+b,在工业界分别变成了比价引擎、记账核心、计划调度器。算法不变,封装变了------从 for 循环变成了 SQL 查询和 API 调用,但核心逻辑一脉相承。
你今天在洛谷上帮小杨做的事------买最便宜的文具、算储蓄罐里有多少钱、按计划做题------和淘宝帮你比价、支付宝帮你记账、Streaks 帮你养成习惯,做的是同一件事:用程序让生活更便捷。
📚 延伸阅读文献
论文与技术文档
- E. F. Codd. A Relational Model of Data for Large Shared Data Banks. Communications of the ACM, 1970. ------ 关系数据库的奠基论文,GROUP BY + MIN/SUM 的理论基础。
- D. E. Knuth. The Art of Computer Programming, Vol. 1: Fundamental Algorithms (3rd Edition). Addison-Wesley, 1997. ------ 数组、链表等基础数据结构的权威论述。
在线资源
- 洛谷. B4450 GESP202512 三级 小杨的智慧购物 . https://www.luogu.com.cn/problem/B4450
- 洛谷. B3867 GESP202309 三级 小杨的储蓄 . https://www.luogu.com.cn/problem/B3867
- 洛谷. B3923 GESP202312 二级 小杨做题 . https://www.luogu.com.cn/problem/B3923
- SQL GROUP BY Tutorial --- W3Schools . https://www.w3schools.com/sql/sql_groupby.asp ------ SQL GROUP BY + MIN/SUM 聚合教程。
- Fibonacci Sequence --- Math is Fun . https://www.mathsisfun.com/numbers/fibonacci-sequence.html ------ 斐波那契序列入门。
- Database Indexing --- GeeksforGeeks . https://www.geeksforgeeks.org/database-indexing/ ------ 数据库索引原理,B+ 树与数组下标的关系。
- 价格比较网站 --- 维基百科 . (Price Comparison Service --- Wikipedia) ------ 比价网站的工作原理与历史。
- Personal Finance Software --- Wikipedia . (Personal Finance Software --- Wikipedia) ------ 个人记账软件的发展史。
推荐教材
-
T. H. Cormen, C. E. Leiserson, R. L. Rivest, C. Stein. Introduction to Algorithms (4th Edition). MIT Press, 2022. ------ 算法圣经,含数组、递推、聚合等基础内容。
-
H. Garcia-Molina, J. Ullman, J. Widom. Database Systems: The Complete Book (2nd Edition). Pearson, 2008. ------ 数据库系统教材,含 GROUP BY、索引、聚合查询。
-
R. Bird. Thinking Functionally with Haskell. Cambridge University Press, 2014. ------ 函数式编程视角的递推与累加,帮助理解算法模式的抽象。
本文标签:#算法 #数组 #分组取最小 #累加器 #斐波那契 #比价 #记账 #习惯养成 #洛谷题解 #信奥 #C++ #入门
本文首发于 CSDN,作者:HugoStudio_SWAN