[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;
}
};
參考文獻: