🎄Advent of Code 2025 挑战全手写代码 Day 3 - 大堂
第三天题目 Lobby,难度 ⭐⭐(2 星),主要考察 贪心算法 (Greedy Algorithm) 和 预处理优化。
📖 题目速览
题目地址:adventofcode.com/2025/day/3
背景: 我们来到了大厅,发现电梯和自动扶梯都断电了。为了启动它们,我们需要利用手头的电池组(Batteries)。输入数据的每一行代表一组电池序列(数字 1-9)。
Part 1:
对于每一行电池(数字序列),我们需要从中按顺序挑选出 2 个电池,使得这两个数字组成的两位数(Joltage)最大。
例如序列 987654321,选 9 和 8 组成 98。
求所有行最大两位数之和。
Part 2: 规则升级!这次我们需要从每一行中按顺序挑选出 12 个电池,使得这 12 个数字组成的十二位数最大。 求所有行最大十二位数之和。
💡 解题思路 (Python 🐍)
这道题本质上是一个 “求字典序最大的固定长度子序列” 的问题。
Part 1: 双指针 vs 后缀最大值 (Suffix Max)
要让两位数 最大,首先 要尽可能大,其次 要尽可能大。 由于只需要选 2 个数,我们可以使用极其高效的 后缀最大值(Suffix Max) 预处理。
- 预处理:倒序遍历数组,记录“从当前位置到末尾”的最大值。
largest_seen[i]表示bank[i+1:]中的最大值。
- 遍历:枚举每一个数作为第一个数 ,然后直接查表得到它后面最大的数作为 。
- 比较:在所有组合中取最大值。
这种方法的时间复杂度是严格的 。
Part 2: 贪心构建 (Greedy Construction)
当需要选 12 个数时,预处理所有组合显然不可行(复杂度爆炸!)。这里我们采用 贪心策略。 因为高位数字对数值大小起决定性作用( 永远大于 ),我们应该从左到右逐位确定数字,每一位都尽可能选最大的。
- 预处理索引:将每个数字(1-9)出现的所有索引位置记录在哈希表(
dict)中,方便 查找。 - 逐位构建:我们需要填 12 个坑。
- 对于第 1 个坑,尝试填
9。如果9存在,且选了它之后,后面的剩余字符足够填满剩下的 11 个坑,那就选它! - 如果
9不行,就试8,以此类推。
- 对于第 1 个坑,尝试填
- 维护区间:
lower_bound:上一个选定数字的索引 + 1(因为必须按顺序选)。upper_bound:当前能选的最远索引(保证后面剩下的数字够用)。
✨ 代码复盘 & 优化思考
在 Part 1 中,我使用了一个特定于 的优化:
# Precompute the max value to the right of each index (suffix max)
largest_seen: list[int] = []
for battery in bank[::-1]:
if not largest_seen:
largest_seen.append(battery)
else:
largest_seen.append(max(battery, largest_seen[-1]))
而在 Part 2 中,为了处理 这种更通用的情况,我切换到了基于索引约束的贪心搜索。这种方法的扩展性更强,如果题目改成选 个数,只需要把循环次数改为 即可。
# Greedily construct the 12-digit number from left to right
for position in range(1, 13):
# Update upper bound to ensure enough digits remain
upper_bound = len(bank) - 12 + position
# Try digits from 9 down to 1
for candidate in range(9, 0, -1):
# ... find first valid index ...
即使是 Part 2 这种 12 个数字的情况,也可以轻松在1秒内完成。
完整代码:访问 github
Happy Coding! 今天也要继续冲榜!🚀