NOJ - T026
[編輯] [转简体] (简体译文)
|
作者:huidong
| 分類:【編程】NOJ 和 C 程序設計習題
[
26 瀏覽
0 評論
3 贊
4 踩
]
概要
倒水問題,聽說是 C++ 經典問題。
正文
注:如果無解會輸出 0
#include <stdio.h> #define min(a, b) (a < b ? a : b) int main() { // 0 < m < n, d < n // 兩個無刻度杯子分別可以裝 m, n 升水,現在需要通過他們配出 d 升水 // 可以清空/裝滿杯子,可以轉移杯子的水(使其一空或滿) unsigned int m, n, d; scanf("%d %d %d", &m, &n, &d); //m = 3, n = 8, d = 4; // 當前水量記錄 unsigned int a = 0, b = 0; // 消耗步驟記錄 // Total: 整個倒水從 (0, 0) 到另一個 (0, 0) 一共可以走幾步 // Min, Max: 正序進行到滿足條件需要的步數最值 unsigned int stepTotal = 0, stepMin = 0, stepMax = 0; do { // 總體趨勢:a 不斷地想轉移到 b 中 // 但是有以下情況: // 1. b 滿了,那就倒掉 // (1, 2 順序不可調換,因爲先滿上會導致 0,n -> m,n 情況,而預期的是 0,n -> 0,0) if (b == n) { b = 0; } // 2. a 空了,那就滿上 else if (a == 0) { a = m; } // 3. 否則,沒有其他選擇,就是把 a 轉到 b else { // b 變成 a + b,但是可能過滿了,所以得跟 n 取最小 // t 表示實際轉移的體積 unsigned int t = min(a + b, n) - b; a -= t; b += t; } // 計步 stepTotal++; if (a == d || b == d) { if (stepMin == 0) // 第一個滿足條件的步數即爲 Min stepMin = stepTotal; stepMax = stepTotal; // Max 可以持續刷新記錄 } } while (!(a == 0 && b == 0)); // 只要不是 a, b 都空了就接着倒水, // 如果空了說明所有走法都走過了。 // 取正序倒序的最小步數(全局最小),如果無解此處會輸出 0 unsigned int globalMin = min(stepMin, stepTotal - stepMax); printf("%u", globalMin); return 0; }
簡單說明:
其實這個倒水的過程只有一條路可以走,就是不斷往另一個杯子裝水(己空則添,彼滿則覆),然後在這個過程中產生各種水量,看看有沒有自己需要的水量。
不難發現,這個過程其實就是 A 添水到 B,B 杯子中不斷重複 b = (b + m) % n,不同於 m, n 的水量是通過 m, n 的餘數實現的。只要 m, n 互質,就能產生 1 ~ n 的所有水量。如果他們不互質,就只能產生數倍於 gcd(m, n) 的水量(gcd 爲求最大公約數函數)。