[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

解题思路

两种思路:

  1. 第一种方法是搜索,使用BFS,三个杯子两两操作,每一步都有6种倾倒方式。这种也能AC,开销大一点。
  2. 第二种是数论。找出其中的数学规律,加以解决。

我们注意到,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 这三个操作的排列组合而已。
例如:

  1. (s,m,n)=(4,3,1) 的倒水方案是: s->m, m->n, n->s
  2. (s,m,m)=(6,5,1) 的倒水方案是: s->m, m->n, n->s, m->n, n->s
  3. (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;
    }
};

参考文献:

https://www.cnblogs.com/ECJTUACM-873284962/p/6750320.html

posted @ 2021-01-11 20:20  与MPI做斗争  阅读(150)  评论(0编辑  收藏  举报