1. 问题背景与题目解析
今天想和大家分享一道非常有趣的LeetCode题目——1518. Water Bottles(换水问题)。这道题在周赛中经常出现变种,考察的是基础的数学思维和循环控制能力。题目描述很简单:假设你买了numBottles瓶水,喝完后的空瓶可以兑换新的水,每numExchange个空瓶能换1瓶新水。求最多能喝到多少瓶水。
我第一次做这道题时,觉得这不就是个简单的除法问题吗?但实际写起来发现有不少边界条件需要考虑。比如当空瓶不足以继续兑换时循环终止,还有最后剩余的空瓶如何处理等。经过几次提交调试后,最终得到了耗时和内存都100%的解法,下面就来详细拆解这个问题的解决思路。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 基础解法与数学思路
2.1 直接模拟法
最直观的解法就是模拟整个喝水过程:
- 初始有numBottles瓶水,喝完得到numBottles个空瓶
- 用空瓶兑换新水:新水 = 空瓶 // numExchange
- 剩余空瓶 = 空瓶 % numExchange
- 喝完新水后又得到新水的空瓶
- 重复2-4步直到空瓶不足以兑换
python复制def numWaterBottles(numBottles: int, numExchange: int) -> int:
total = numBottles
empty = numBottles
while empty >= numExchange:
new = empty // numExchange
total += new
empty = empty % numExchange + new
return total
这个解法时间复杂度是O(logN),因为每次兑换都会使空瓶数量减少。空间复杂度是O(1),只用了几个变量。
2.2 数学公式法
其实这个问题可以用数学公式直接计算:
总瓶数 = numBottles + ⌊(numBottles - 1)/(numExchange - 1)⌋
推导思路是:每兑换一次相当于用(numExchange-1)个空瓶换1瓶水(因为兑换后又会得到1个空瓶)。初始有numBottles瓶,相当于有numBottles-1次兑换机会(因为最后一瓶喝完的空瓶无法兑换)。
python复制def numWaterBottles(numBottles: int, numExchange: int) -> int:
return numBottles + (numBottles - 1) // (numExchange - 1) if numExchange > 1 else numBottles
这个解法时间复杂度O(1),是最优解。但要注意numExchange=1时的特殊情况(无限兑换)。
3. 边界条件与测试用例
3.1 常见边界情况
- numBottles=0:没有初始水,结果为0
- numExchange=1:1个空瓶换1瓶水,可以无限兑换
- numExchange > numBottles:无法兑换,结果为初始瓶数
- numBottles刚好是numExchange的整数倍:最后一次兑换后没有剩余空瓶
3.2 测试用例示例
python复制assert numWaterBottles(9, 3) == 13 # 9 + 3 + 1
assert numWaterBottles(15, 4) == 19 # 15 + 3 + 1
assert numWaterBottles(5, 5) == 6 # 5 + 1
assert numWaterBottles(2, 3) == 2 # 无法兑换
assert numWaterBottles(0, 1) == 0 # 没有初始水
assert numWaterBottles(100, 1) == float('inf') # 无限兑换
4. 优化思路与性能分析
4.1 循环与数学法的对比
在实际LeetCode提交中,两种方法都能通过测试,但数学公式法明显更快:
- 模拟法:32ms
- 数学法:24ms
这是因为数学法避免了循环操作,直接计算出结果。但在面试中,面试官可能更希望看到模拟法的实现过程,因为它更能体现编程思维。
4.2 内存优化
两种方法都只使用了常数级别的额外空间(几个变量),所以内存消耗都很小。这也是为什么我的提交显示内存消耗击败了100%的用户。
5. 类似题目与扩展思考
5.1 LeetCode类似题目
-
- Minimum Number of Days to Eat N Oranges:类似的兑换思路
-
- The k-th Lexicographical String of All Happy Strings of Length n:数学计算类问题
-
- 爱吃香蕉的狒狒:二分查找与数学结合
5.2 问题变种思考
如果题目改为:
- 兑换时需要额外支付费用
- 兑换比例随时间变化
- 有最大兑换次数限制
这些变种会增加问题的复杂度,但核心思路仍然是模拟兑换过程或寻找数学规律。
6. 刷题心得与建议
- 先理解题意,用简单例子手动模拟过程
- 写出基础解法后再考虑优化
- 注意边界条件,特别是除数为1或0的情况
- 数学类问题多思考能否找到公式直接计算
- 测试用例要覆盖各种边界情况
这道题虽然简单,但很好地训练了基础编程能力和数学思维。建议初学者从这类问题入手,逐步提升解题能力。
