题解:洛谷 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
【核心思想】
-
问题分析:给定三个桶,每个桶有容积 \(c_i\) 和当前牛奶量 \(m_i\)。按照固定周期(桶1→桶2→桶3→桶1→…)进行 \(100\) 次倒奶操作,每次将当前桶的奶尽可能倒入下一个桶(直到源桶倒空或目标桶倒满)。求 \(100\) 次操作后每个桶的牛奶量。
-
算法选择:
- 直接模拟:由于操作次数固定为 \(100\) 次,且每次操作逻辑简单(比较、减法、赋值),直接按规则模拟即可
- 循环周期优化(可选):虽然 \(100\) 次可以直接模拟,但注意到状态空间有限(三个桶的牛奶量组合),若次数极大可检测循环节进行优化
-
关键步骤:
- 初始化:读取三个桶的容积 \(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]\)
-
时间/空间复杂度:
- 时间复杂度:\(O(100) = O(1)\),固定 \(100\) 次操作
- 空间复杂度:\(O(1)\),仅需存储三个桶的状态
-
模拟算法的核心思想:
- 按规则逐步执行:不寻找数学公式,而是严格按照题目描述的规则逐步推演,利用计算机的快速计算能力处理重复操作
- 状态转移的确定性:每次操作只依赖当前状态,无随机性,模拟结果唯一确定
- 周期取模:通过 \(\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
浙公网安备 33010602011771号