每周一道算法题005:切木棒

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了每周一道算法题005:切木棒相关的知识,希望对你有一定的参考价值。

问题:

假设要把长度为n厘米的木棒切分为1厘米长的小段,但是1根木棒只能由1人切分,当木棒被切分为3段后,可以同时由3个人分别切分木棒。求最多有m个人时,最少要切分几次。

譬如n=8,m=3时如下图所示,切分4次就可以了。
技术图片

求当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

以上是关于每周一道算法题005:切木棒的主要内容,如果未能解决你的问题,请参考以下文章

每周一道算法题:兑换零钱

每周一道算法题002:四则运算

每周一道算法题006:抽签组合

每周一道算法题003:翻牌

每周一道算法题011:最长公共子串

每周一道算法题010:扫地机器人路径统计