每週一道算法題005:切木棒

問題:

假設要把長度爲n釐米的木棒切分爲1釐米長的小段,但是1根木棒只能由1人切分,當木棒被切分爲3段後,可以同時由3個人分別切分木棒。求最多有m個人時,最少要切分幾次。

譬如n=8,m=3時如下圖所示,切分4次就可以了。
每週一道算法題005:切木棒

求當n=20,m=3時的最少切分次數。
求當n=100,m=5時的最少切分次數。

思路:

這道題最難的不是算法,而是理解題意。
木棒剛開始只有1根,題目規定“1根木棒只能由1人切分”,此時由1人切分後,變成2根;
2根木棒再做切分,也要滿足“1根木棒只能由1人切分”的規則,所以此時由2個人,每人切1根,變成了4根;
隨後,題目說“當木棒被切分爲3段後,可以同時由3個人分別切分木棒”,也就是說,
當木棒根數達到上限人數後,每一次便只能切分出上限人數的根數了。
而在這之前,每次都有足夠的人切木棒,每次也都是一分爲二,木棒的數量都是翻倍遞增

用文字來描述便是:

當前木棒數量 >= 人數上限時,新木棒數量 = 原有木棒數量 + 上限人數;
當前木棒數量 < 人數上限時,新木棒數量 = 原有木棒數量 * 2;
當新木棒數量大於等於木棒長度時,切分結束。

此題可以用遞歸和循環兩種方法來解。

遞歸:

遞歸的結束條件便是current(當前的木棒數)>= n (木棒長度)。
當current (當前的木棒數)< m(人數上限)時,current = current * 2。
當current(當前的木棒數)>= m (人數上限)時,current = current + m;
每次遞歸時,就將當前木棒數通過參數傳入下一次遞歸中,直至滿足退出條件。

循環:

循環的結束條件與遞歸一樣,也是當current(當前的木棒數)>= n (木棒長度)時退出,即是current < n時一直執行循環。
當current (當前的木棒數)< m(人數上限)時,current = current + current。
當current(當前的木棒數)>= m (人數上限)時,current = current + m;
每執行一次循環,count(循環計數,即切分次數)加1。

解答:

php

// 遞歸
// m 爲上限人數
// n 爲木棒長度
// current 爲當前木棒數量
function cutBar($m, $n, $current)
{
    if ($current >= $n) {
        return 0;
    } elseif ($current < $m) {
        return 1 + cutBar($m, $n, $current * 2);
    } else {
        return 1 + cutBar($m, $n, $current + $m);
    }
}

// 循環
function cutBar2($m, $n)
{
    $current = 1;// 初始木棒數
    $count = 0; // 切分計數
    while ($current < $n) {
        $current += $current < $m ? $current : $m;
        $count++;
    }
    return $count;
}

$rs = cutBar(3, 8, 1);
echo $rs."\n";

$rs = cutBar(3, 20, 1);
echo $rs."\n";

$rs = cutBar(5, 100, 1);
echo $rs."\n";

$rs = cutBar2(3, 8);
echo $rs."\n";

$rs = cutBar2(3, 20);
echo $rs."\n";

$rs = cutBar2(5, 100);
echo $rs."\n";

輸出:

4
8
22
4
8
22

golang

package main

import "fmt"

func main() {
    rs := cutBar(3, 8, 1)
    fmt.Println(rs)

    rs = cutBar(3, 20, 1)
    fmt.Println(rs)

    rs = cutBar(5, 100, 1)
    fmt.Println(rs)

    rs = cutBar2(3, 8)
    fmt.Println(rs)

    rs = cutBar2(3, 20)
    fmt.Println(rs)

    rs = cutBar2(5, 100)
    fmt.Println(rs)
}

func cutBar(m, n, current int) int {
    if current >= n {
        return 0
    } else if current < m {
        return 1 + cutBar(m, n, current*2)
    } else {
        return 1 + cutBar(m, n, current+m)
    }
}

func cutBar2(m, n int) int {
    current := 1
    count := 0
    for current < n {
        if current < m {
            current += current
        } else {
            current += m
        }
        count++
    }
    return count
}

輸出:

4
8
22
4
8
22
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章