返回文章列表

饮料换购

2026年5月22日5 分钟阅读

题目背景与核心痛点

这是一道在算法比赛中非常经典的“饮料换购”问题。题目看似是一个需要不断模拟“换饮料 \rightarrow 喝饮料 \rightarrow 产生新瓶盖 \rightarrow 再换饮料”的动态循环过程,但如果我们跳出纯粹的代码模拟,从资源消耗的本质去思考,它其实可以转化为一个极其优雅的数学模型。


解题思路:从循环模拟到数学降维打击

方法一:循环模拟法(直观逻辑)

我们引入两个变量:total_drunk(已喝到的饮料总数)和 caps(当前拥有的瓶盖数)。 初始状态下,买几瓶就能喝几瓶,并产生同等数量的瓶盖:total_drunk = n; caps = n;。 只要满足 caps >= 3,就不断进行如下循环:

  1. 计算本轮可以兑换的新饮料:new_drinks = caps / 3
  2. 累加到总饮用量:total_drunk += new_drinks
  3. 更新下一轮的瓶盖数:caps = (caps % 3) + new_drinks(即这一轮换饮料剩下的瓶盖 + 新喝完产生的瓶盖)

该方法在 n<1000n < 1000 的情况下执行速度很快,但如果数据量拓展到 10910^9 级别,循环机制就会带来额外的计算开销。

方法二:数学公式法(等效替代思维)

我们可以通过 “净消耗分析” 来实现 O(1)O(1) 的降维打击:

  1. 寻找等效关系:根据规则,3 个瓶盖可以换 1 瓶新饮料。而换回来的新饮料喝完后会退回 1 个瓶盖。这意味着,在这个微型循环里,你每多喝一瓶新饮料,手里实质上净减少31=23 - 1 = 2 个瓶盖。
  2. 处理“不能赊账”的边界:既然每消耗 2 个瓶盖就能多喝 1 瓶,那直接用 n/2n / 2 行不行?不行。因为老板不接受赊账,如果你手里只剩 2 个瓶盖,虽然它们“等价”于一瓶饮料,但你由于凑不够 3 个瓶盖的“入场券”,游戏会强制终止。也就是说,在全场的最后,不论如何你手里都会剩下一个注定无法被消耗的“保底”瓶盖。
  3. 推导公式:既然最后必定剩 1 个瓶盖,那我们索性先把这 1 个瓶盖藏起来。剩下真正能拿去参与“每 2 个净消耗换一瓶”规则的有效瓶盖数就是 n1n - 1 个。 因此,额外能换到的饮料数就是 n12\lfloor \frac{n - 1}{2} \rfloor

加上初始购买的 nn 瓶,最终能够喝到的饮料总数为: total_drunk=n+n12total\_drunk = n + \lfloor \frac{n - 1}{2} \rfloor


C 语言代码实现

方案一:循环模拟实现(适合初学者,逻辑直观)

#include <stdio.h>

int main() {
    int n;
    if (scanf("%d", &n) == 1) {
        int total_drunk = n; // 初始喝到的饮料数
        int caps = n;        // 初始拥有的瓶盖数
        
        // 只要瓶盖不少于 3 个,就可以继续换
        while (caps >= 3) {
            int new_drinks = caps / 3;   // 本轮用完所有能换的盖子
            total_drunk += new_drinks;   // 累加喝到的饮料
            caps = (caps % 3) + new_drinks; // 剩下的盖子 + 喝完新产生的盖子
        }
        
        printf("%d\n", total_drunk);
    }
    return 0;
}

方案二:数学公式实现(效率高)

#include <stdio.h>

int main() {
    int n;
    
    // 严谨读取输入,确保成功接收到一个整数
    if (scanf("%d", &n) == 1) {
        
        // 运用数学公式直接计算出最终结果
        int total_drunk = n + (n - 1) / 2;
        
        // 打印输出结果
        printf("%d\n", total_drunk);
    }
    
    return 0;
}

💡 蓝桥杯通关·快速复盘

  • 一句话题眼(数学降维打击) 核心本质是 “等效替代与净消耗”。3个盖子换1瓶饮料(带1个盖子),等价于 “每净消耗 2 个盖子就能多喝 1 瓶”。由于老板不给赊账,最后必定剩 1 个保底盖子无法消耗。因此先扣掉这 1 个盖子,其余套用 O(1)O(1) 通项公式直接秒杀: 总数=n+n12\text{总数} = n + \lfloor \frac{n - 1}{2} \rfloor

  • 防错指南

    1. 输入防御:严格使用 if (scanf("%d", &n) == 1) 进行安全拦截,防止蓝桥杯盲测脏数据。
    2. 大数越界防御:如果题目涉及大数(如 n>109n > 10^9),直接用 int 可能会在计算公式中间整型溢出。稳妥起见,比赛时建议直接开 long long 配合 %lld 读写。
    3. 防范“除零”崩溃:套用通用拓展公式 nBAB\lfloor \frac{n - B}{A - B} \rfloor 时,必须确保分母 ABA \neq B。如果 A==BA == B(即等价于无消耗白嫖),需要特判处理,防止程序因“除以 0”直接 Runtime Error。

发表评论