感 谢 大 佬 们 的 点 赞 O r z \color{purple}{感谢大佬们的点赞~Orz} 感谢大佬们的点赞 Orz
题目描述
农业,尤其是生产牛奶,是一个竞争激烈的行业。
Farmer John 发现如果他不在牛奶生产工艺上有所创新,他的乳制品生意可能就会受到重创!
幸运的是,Farmer John 想出了一个好主意。
他的三头获奖的乳牛,Bessie、Elsie 和 Mildred,各自产奶的口味有些许不同,他打算混合这三种牛奶调制出完美的口味。
为了混合这三种不同的牛奶,他拿来三个桶,其中分别装有三头奶牛所产的奶。
这些桶可能有不同的容积,也可能并没有完全装满。
然后他将桶 1 1 1 的牛奶倒入桶 2 2 2,然后将桶 2 2 2 中的牛奶倒入桶 3 3 3,然后将桶 3 3 3 中的牛奶倒入桶 1 1 1,然后再将桶 1 1 1 的牛奶倒入桶 2 2 2,如此周期性地操作,共计进行 100 100 100 次(所以第 100 100 100 次操作会是桶 $1 $倒入桶 2 2 2)。
当 Farmer John 将桶 a a a 中的牛奶倒入桶 b b b 时,他会倒出尽可能多的牛奶,直到桶 a a a 被倒空或是桶 b b b 被倒满。
请告诉 Farmer John 当他倒了 100 100 100 次之后每个桶里将会有多少牛奶。
输入格式
输入文件的第一行包含两个空格分隔的整数:第一个桶的容积 c 1 c_1 c1,以及第一个桶里的牛奶量 m 1 m_1 m1。
第二和第三行类似地包含第二和第三个桶的容积和牛奶量。
输出格式
输出三行,给出倒了 100 100 100 次之后每个桶里的牛奶量。
数据范围
1 ≤ c 1 , m 1 ≤ 1 0 9 \color{Red}{1≤c_1,m_1≤10^9} 1≤c1,m1≤109
输入样例
10 3
11 4
12 5
输出样例
0
10
2
样例解释
在这个例子中,每倒一次之后每个桶里的牛奶量如下:
初始状态: 3 4 5
1. 桶1->2: 0 7 5
2. 桶2->3: 0 0 12
3. 桶3->1: 10 0 2
4. 桶1->2: 0 10 2
5. 桶2->3: 0 0 12
(之后最后三个状态循环出现……)
算法1
(朴素版暴力迭代模拟) O ( 1 ) \color{Red}{O(1)} O(1)
-
读入3个桶的容积和牛奶量
-
从第一个桶开始
idx = 1
,则下一个桶next = (idx + 1) % 4 ? idx + 1 : 1
-
设下一个桶为 n e x t next next,则 n e x t next next桶的容积为 n e x t ( v ) next(v) next(v), n e x t next next桶的牛奶量为 n e x t ( w ) next(w) next(w),则有
时间复杂度: O ( 1 ) O(1) O(1)
C++ 代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef pair<int, int> PII;//容积,牛奶量
PII a[4];
int main()
{
for (int i = 1; i <= 3 ; i++ )//读入3个桶的容积和牛奶量
{
int x, b;
scanf("%d%d", &x, &b);
a[i] = {x, b};
}
int idx = 1;//记录一下从第一个桶开始
for (int i = 0; i < 100; i ++ )//迭代100次
{
int next = (idx + 1) % 4 ? idx + 1 : 1;//应该往哪个桶倒idx->next
int v = a[next].first;//查看一下next桶的容积
int w = a[next].second;//查看一下next桶的牛奶量
if (v == w)//如果next桶的容积等于牛奶量了,说明不需要再倒牛奶了~
{
}
else if (a[idx].second + w <= v)//如果idx桶的牛奶量与next桶的牛奶量要小于next桶的容积
{
a[next].second = a[idx].second + w;//更新next桶的牛奶量
a[idx].second = 0;//idx桶的牛奶量为0
}
else //如果idx桶的牛奶量与next桶的牛奶量要大于next桶的容积
{
a[idx].second -= v - w;//更新idx桶的剩余的牛奶量
a[next].second = v;//next桶的牛奶量则为其容积
}
idx = next;//idx桶指向下一个桶(next)
}
for (int i = 1; i <= 3; i ++ )//输出迭代100次的答案~
printf("%d\n", a[i].second);
return 0;
}
算法2
(找环进行优化操作)
由我们的样例解释可知
初始状态: 3 4 5
1. 桶1->2: 0 7 5
2. 桶2->3: 0 0 12
3. 桶3->1: 10 0 2
4. 桶1->2: 0 10 2
5. 桶2->3: 0 0 12
(之后最后三个状态循环出现……)
可以找到一个
环
\color{Red}{环}
环,使得只用计算后续
经
过
多
少
个
环
\color{Red}{经过多少个环}
经过多少个环后
再
经
过
多
少
步
得
到
的
最
终
状
态
\color{Red}{再经过多少步得到的最终状态}
再经过多少步得到的最终状态~
如下图所示:
我们只需要用一个字符串记录三个桶牛奶量的状态,利用map
看之前是否出现过,出现过的话,我们就找到了一个环直接退出循环,计算经过``p``圈后还需走多少步达到100次,最终输出答案即可
这样我们就不用枚举100次了~
C++ 代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
using namespace std;
typedef pair<int, int> PII;//容积,牛奶量
PII a[4];
unordered_map<string, int> state;
int r;
string s;
int main()
{
for (int i = 1; i <= 3 ; i++ )
{
int x, b;
scanf("%d%d", &x, &b);
a[i] = {x, b};
}
int idx = 1;
for (int i = 1; i <= 100; i ++ )
{
int next = (idx + 1) % 4 ? idx + 1 : 1;
int v = a[next].first;
int w = a[next].second;
if (v == w)
{
}
else if (a[idx].second + w <= v)
{
a[next].second = a[idx].second + w;
a[idx].second = 0;
}
else
{
a[idx].second -= v - w;
a[next].second = v;
}
idx = next;
/*上方的注释请看朴素版的算法~*/
//合成一下状态(桶1的牛奶量, 桶2的牛奶量, 桶3的牛奶量)
s = to_string(a[1].second) + " " + to_string(a[2].second) + " " + to_string(a[3].second);
if (!state.count(s))//如果该状态在之前没有,则插入该状态
state[s] = i;
else //若有,则找到环的大小,并退出循环
{
r = (100 - i) % (i - state[s]);
break;
}
}
int x = state[s] + r ? state[s] + r : 1;//找到最终状态的位置~
for (auto t : state)//找到最终状态`
if (t.second == x)
{
s = t.first;
break;
}
//处理一下输出结果~
int t = s.find(' ');
for (int i = 0; i < t; i ++ ) printf("%c", s[i]);
s[t] = '.';
puts("");
int p = s.find(' ');
for (int i = t + 1; i <= p; i ++ ) printf("%c", s[i]);
s[p] = '.';
puts("");
int len = s.length();
for (int i = p + 1; i < len; i ++ ) printf("%c", s[i]);
puts("");
return 0;
}