题解:洛谷 P5116 Mixing Milk

【题目来源】

洛谷:P5116 [USACO18DEC] Mixing Milk B - 洛谷

【题目描述】

农业,尤其是生产牛奶,是一个竞争激烈的行业。Farmer John 发现如果他不在牛奶生产工艺上有所创新,他的乳制品生意可能就会受到重创!

幸运的是,Farmer John 想出了一个好主意。他的三头获奖的乳牛,Bessie、Elsie 和 Mildred,各自产奶的口味有些许不同,他打算混合这三种牛奶调制出完美的口味。

为了混合这三种不同的牛奶,他拿来三个桶,其中分别装有三头奶牛所产的奶。这些桶可能有不同的容积,也可能并没有完全装满。然后他将桶 \(1\) 的牛奶倒入桶 \(2\),然后将桶\(2\)中的牛奶倒入桶\(3\),然后将桶\(3\)中的牛奶倒入桶 \(1\),然后再将桶\(1\) 的牛奶倒入桶\(2\),如此周期性地操作,共计进行\(100\)次(所以第\(100\)次操作会是桶\(1\)倒入桶 \(2\))。当 Farmer John 将桶a中的牛奶倒入桶\(b\)时,他会倒出尽可能多的牛奶,直到桶\(a\)被倒空或是桶 \(b\)被倒满。

请告诉 Farmer John 当他倒了\(100\)次之后每个桶里将会有多少牛奶。

【输入】

输入文件的第一行包含两个空格分隔的整数:第一个桶的容积\(c_1\),以及第一个桶里的牛奶量 \(m_1\)\(c_1\)\(m_1\)均为正,并且不超过 \(10^9\)。第二和第三行类似地包含第二和第三个桶地容积和牛奶量。

【输出】

输出三行,给出倒了\(100\) 次之后每个桶里的牛奶量。

【输入样例】

10 3
11 4
12 5

【输出样例】

0
10
2

【核心思想】

  1. 问题分析:给定三个桶,每个桶有容积 \(c_i\) 和当前牛奶量 \(m_i\)。按照固定周期(桶1→桶2→桶3→桶1→…)进行 \(100\) 次倒奶操作,每次将当前桶的奶尽可能倒入下一个桶(直到源桶倒空或目标桶倒满)。求 \(100\) 次操作后每个桶的牛奶量。

  2. 算法选择

    • 直接模拟:由于操作次数固定为 \(100\) 次,且每次操作逻辑简单(比较、减法、赋值),直接按规则模拟即可
    • 循环周期优化(可选):虽然 \(100\) 次可以直接模拟,但注意到状态空间有限(三个桶的牛奶量组合),若次数极大可检测循环节进行优化
  3. 关键步骤

    • 初始化:读取三个桶的容积 \(c[0..2]\) 和当前牛奶量 \(m[0..2]\)
    • 周期模拟(循环 \(i\)\(1\)\(100\)):
      • 确定当前源桶下标 \(\text{mark} = (i-1) \bmod 3\),目标桶下标 \((\text{mark} + 1) \bmod 3\)
      • 计算两桶牛奶总量 \(\text{tmp} = m[\text{mark}] + m[(\text{mark}+1)\%3]\)
      • 判断溢出:若 \(\text{tmp} > c[(\text{mark}+1)\%3]\)(目标桶装不下):
        • 目标桶装满:\(m[(\text{mark}+1)\%3] = c[(\text{mark}+1)\%3]\)
        • 源桶剩余:\(m[\text{mark}] = \text{tmp} - c[(\text{mark}+1)\%3]\)
      • 否则(目标桶能装下):
        • 目标桶接收全部:\(m[(\text{mark}+1)\%3] = \text{tmp}\)
        • 源桶清空:\(m[\text{mark}] = 0\)
    • 输出结果:依次输出 \(m[0], m[1], m[2]\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(100) = O(1)\),固定 \(100\) 次操作
    • 空间复杂度:\(O(1)\),仅需存储三个桶的状态
  5. 模拟算法的核心思想

    • 按规则逐步执行:不寻找数学公式,而是严格按照题目描述的规则逐步推演,利用计算机的快速计算能力处理重复操作
    • 状态转移的确定性:每次操作只依赖当前状态,无随机性,模拟结果唯一确定
    • 周期取模:通过 \(\text{mark} = (i-1) \bmod 3\) 实现三个桶的循环轮转,避免冗长的条件判断
    • 总量守恒验证:三桶牛奶总量始终不变(\(\sum m_i = \text{const}\)),可作为调试检验
    • 适用于规则明确、操作次数可控、状态转移确定的过程模拟问题

【解题思路】

【算法标签】

普及- #模拟

【代码详解】

#include <bits/stdc++.h>
using namespace std;
struct node {
    int c, m;
}p[5];
int main()
{
    for (int i=0; i<3; i++) {  // 输入3个痛的容积和牛奶量
        cin >> p[i].c >> p[i].m;
    }
    int mark = 0;  // 定义下标mark
    for (int i=1; i<=100; i++) {  // 执行100次
        int tmp = p[mark].m+p[(mark+1)%3].m;  // 先统计当前桶和下一个桶的牛奶量总和
        if (tmp>p[(mark+1)%3].c) {  // 如果牛奶量大于下一个桶的容积
            p[(mark+1)%3].m = p[(mark+1)%3].c;  // 下一个桶将被装满
            p[mark].m = tmp - p[(mark+1)%3].c;  // 而当前桶只能倒出部分,留下部分
        } else {  // 牛奶量小于等于下一个桶的容积
            p[(mark+1)%3].m = tmp;  // 下一个桶就装下所有
            p[mark].m = 0;  // 当前桶就会被清空
        }
        mark++;  // 更新下标
        mark %=3 ;  // 下标对3取余
    }
    for (int i=0; i<3; i++) {  // 顺序打印3个桶的牛奶量
        cout << p[i].m << endl;
    }
    return 0;
}

【运行结果】

10 3
11 4
12 5
0
10
2
posted @ 2026-08-29 09:37  团爸讲算法  阅读(4)  评论(0)    收藏  举报