[LintCode 1674.] 倒可乐
LintCode 1674. 倒可乐
CAT 专属题目 中等难度题
替代链接 HDU 1495.
题目描述
给定一个容积为 s 的装满可乐的瓶子和两个容积分别为 n 和 m 的空杯子, 其中 n + m = s. 问能否通过在三个容器之间来回倒可乐来平分这 s 体积的可乐? 如果可以, 返回最少倾倒可乐的次数; 反之返回 -1.
由于瓶子和杯子上没有刻度, 所以当你从一个容器倒可乐到另一个容器中时, 只能一直倒可乐直到一个容器空了或者另一个满了为止.
样例
样例 1:
输入: s = 4, n = 1, m = 3
输出: 3
解释:
首先, 从瓶子倒3体积的可乐到第二个杯子, 瓶子和两个杯子中分别有 1, 0, 3 体积的可乐.
然后, 倒1体积的可乐从第二个杯子到第一个杯子, 此时三个容器分贝有 1, 1, 2 体积的可乐.
最后, 倒1体积的可乐从第一个杯子到瓶子, 此时瓶子和第二个杯子中都有 2 体积的可乐, 平分.
样例 2:
输入: s = 7, n = 4, m = 3
输出: -1
解释: 7 体积的可乐不能被平分.
注意事项
s <= 100
解题思路
两种思路:
- 第一种方法是搜索,使用BFS,三个杯子两两操作,每一步都有6种倾倒方式。这种也能AC,开销大一点。
- 第二种是数论。找出其中的数学规律,加以解决。
我们注意到,s = m + n
,从而可以得出结论 gcd(m, n) == gcd(s, m, n)
。
如果
m == n
,只需要倒一次就可以了。
如果m != n
不妨假设m > n
,那么最后平分水的时候,一半在s中,一半在m中,因为n的体积不到瓶子的一半。
s、m、n 之间的倒水,其实就是辗转相减,最后能减出的最小单位是gcd(m, n)
。
设 m 和 n 各自净倒水的次数分别为 x 和 y,即各自倒出水次数减去倒入水次数之差为 x 和 y,则两个杯子倒水次数之和最少为 s / gcd(m,n)
。
下面的推导使用 a 和 b 表示的 m 和 n:
由于x 和 y 不可能同正同负,必然一个正一个负。
所有的操作其实都是 s->m
m->n
n->s
这三个操作的排列组合而已。
例如:
(s,m,n)=(4,3,1)
的倒水方案是:s->m, m->n, n->s
。(s,m,m)=(6,5,1)
的倒水方案是:s->m, m->n, n->s, m->n, n->s
。(s,m,n)=(8,5,3)
的倒水方案是:s->m, m->n, n->s, m->n, s->m, m->n, n->s
。
可以看到,每一次两个杯子之间的操作,都伴随着一次大瓶子的辅助使用。
我们把倒水操作两两一组划分,可以看到每个组要么是 m/n 的净倒出组,要么是 m/n 的净倒入组,没有任何一组是只有m和n都倒出倒入相等次数的。
并且最后一组只有半个,所以总步数是 (c + d) / 2 * 2 - 1 = s / gcd(m, n) - 1
。
参考代码
class Solution {
public:
/**
* @param s: the volume of cola
* @param n: the volume of the first cup
* @param m: the volume of the second cup
* @return: the minimum number of times to be inverted
*/
int getMinTimes(int s, int n, int m) {
// Write your code here
s /= __gcd(n,m);
if (s % 2 == 0) return s-1;
return -1;
}
};
参考文献: