匯東網


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 爲求最大公約數函數)。



[ 3] [ 4]


 評論區  0 條評論

+ 添加評論