zoukankan      html  css  js  c++  java
  • 吃豆子过桥问题(转)

    转载博客:http://www.cnblogs.com/webary/p/4827688.html

    吃豆子过桥问题

      本题来自于百度校招面试题,通过一个简单的智力问题理解递归问题的解法。

      一:问题描述

      一个人要过一座80米的桥,每走一米需要吃一颗豆子,他最多可以装60颗豆子,问最少需要吃多少颗豆子才能走完桥?

      二:初步分析

      1.一趟(不折回)最多只能走60米豆子就会被吃完;

      2.如果有折回,必须保证能够返回到有豆子的地点,且在折回点放下的豆子尽量多;

      3.尽可能少的折回(次数和折回的距离都要少,毕竟一趟折回就要多消耗一个来回的豆子);

      4.折回点的豆子数量要求至少能够走完剩余的部分;

      三:具体分析

      1.由条件1得一趟走不完,由条件3我们可以考虑折回尽量少的次数能否走完。

      先考虑只折回一次,那么此时就要求得折回点的位置。

      2.由条件3得折回的距离尽量短,那么可以考虑最短的折回距离,很明显应该是20米处;设折回点距离起始位置x米,则此时x=20;那么在x处,需要至少有60颗豆子,一次性拿60颗豆子走完最后一程。

      那么问题就来了,怎么在x处囤积60颗豆子呢,假使他第一次信心满满的拿着60颗豆子,走到x处就只剩下40颗了,现在不够走完剩余的全程啊,只能放下一部分折回去再取了,那放多少呢,很明显由条件2得我们需要放下20颗,拿着20颗刚好可以折回到起点,继续拿着60颗豆子第二次来到x处,此时手上还有40颗,再捡起第一次放的20颗,共60颗豆子刚好可以走完全程。

      在这种情况下,我们易知,他共走了(20)*3+(60)= 120米,故吃掉120颗豆子。

      四:问题拓展

      如果桥长81米呢?

      此时我们如果还是只折回一次的话,在21米处还是得有60颗豆子,也就是需要搬运60颗豆子到21米处;分析得不可能只折回一次就搬运60颗豆子到21米处,因此需要再设置一个折返点y,此时x=21,0<y<x;

      刚才考虑了80米的情况,那么我们可以假设现在在80米的基础上加了1米,因此可以设y=1,即先折回两趟到1米处,这样y处就有60*3-5*(1)=175颗豆子,再从y到x折返1趟,x处就有60*2-3*(20)=60颗豆子,最后从x出发拿着60颗豆子就可以愉快的走过桥了。

      因此他共走过5*(1)+3*(20)+(60) = 125米,故吃掉125颗豆子。

      五:再次拓展

      如果桥长n米,最多装m颗豆子,最少消耗f(n,m)与n和m的关系是什么呢?

      此时问题突然就变得很复杂了,别急,我们分析一下刚才的思路,桥长从80米到81米,就是要在1米处放足够多豆子(当然只要不小于桥长80米时的消耗就行),那么这些豆子又需要从起点处运到1米处,那么在这1米内又需要消耗多少豆子呢?

      我们可以考虑先把可装的最大豆子数m固定(假设还是60),当桥长0<n<=60米时,f(n,m) = n;

      当n=61时,需要在1米处囤积f(n-1,6)即f(60,60)=60颗豆子,囤积过程中需要往返一次,第一次到达1米处留下58颗豆子赶紧回去再取60颗到达1米处还剩59颗,现在有117颗足够走完最后60米。消耗豆子1*2+(1)+60 = 63颗豆子;

      当n=62时,需要在1米处囤积f(n-1,m)即f(61,60)=63颗豆子,囤积过程中需要往返一次,第一次到达1米处留下58颗豆子赶紧回去再取60颗到达1米处还剩59颗,现在有117颗足够走完最后61米。消耗豆子1*2+(1)+63 = 66颗豆子;

      以此类推...

      那么问题来了,细心的你肯定发现了上面那种情况都是往返一次的,但是不是每次都只往返一次,比如当n=81时,该怎么确定往返的次数呢?

      分析上面的递推关系我们可以知道,每次往返的趟数与f(n-1,m)的大小有关。分析往返过程易知:最后一次到达1米处剩59颗豆子,之前每次到达1米处可以放下58颗豆子,假设之前到达了1米处T次,则有:

      59 + 58 * T >= f(n-1,60)  ==>  T >= [f(n-1,60)-59]/58

      由于T必须是整数,故T = ceil((f(n-1,60)-59) / 58),ceil(x)表示对x向上取整,即不小于x的最小整数

      另一种表示方式为T = floor( (f(n-1,60)-2) / 58 ),这种写法是为了方便编程实现,整型数的除法会自动向下取整

      之前往返T次消耗掉2T颗豆子,最后一次到达1米处消耗1颗豆子,故总消耗:f(n-1,60) + 2T + 1颗豆子

      这时候我们考虑将固定为60的m扩展为任意m,则有:f(n,m) = f(n-1,m) + ceil( f(n-1,m)-(m-1)) / (m-2) ) * 2+ 1 = f(n-1,m) + (f(n-1,m)-2) / (m-2) * 2+ 1 ;

      好了到这里,这个问题也彻底解决完了,现在就让我们用简短的代码来实现这个过程吧!

      六:编程实现   

    复制代码
     1 #include<cmath>
     2 #include<iostream>
     3 using namespace std;
     4 typedef unsigned long long int64;
     5 
     6 //参数说明:length为桥的长度,maxNum为最大可带的豆子数
     7 //递归实现
     8 int64 getMinConsume(int length, int maxNum) {
     9     if(length <= maxNum)
    10         return length;
    11     int64 get_n_1 = getMinConsume(length-1,maxNum); //上一次的豆子消耗
    12     return get_n_1 + (get_n_1-2)/(maxNum-2) * 2 + 1;
    13 }
    14 //第二种公式实现——递归
    15 int64 getMinConsume2(int length, int maxNum) {
    16     if(length <= maxNum)
    17         return length;
    18     int64 get_n_1 = getMinConsume2(length-1,maxNum);  //上一次的豆子消耗
    19     return get_n_1 + ceil((double)(get_n_1-maxNum+1)/(maxNum-2)) * 2 + 1;
    20 }
    21 //非递归的实现方式
    22 int64 getMinConsume_NoRecursive(int length, int maxNum) {
    23     if(length <= maxNum)
    24         return length;
    25     int64 result = maxNum, i;
    26     for(i=maxNum; i<length; i++)
    27         result += (result-2)/(maxNum-2) * 2 + 1;
    28     return result;
    29 }
    30 
    31 int main()
    32 {
    33     int maxNum=60, length;
    34     for(length=50; length<201; length++)
    35         cout<<length<<"米至少消耗"<<getMinConsume2(length,maxNum)<<"颗豆子!"<<endl;
    36     return 0;
    37 }
    38 
    39 int main2()
    40 {
    41     while(1){
    42         cout<<"请输入最大可带的豆子数量和桥的长度: ";
    43         int maxNum, length;
    44         if(!(cin>>maxNum>>length))
    45             break;
    46         cout<<"至少消耗"<<getMinConsume_NoRecursive(length,maxNum)<<"颗豆子!"<<endl;
    47     }
    48     return 0;
    49 }
    复制代码
  • 相关阅读:
    2020 浏览器市场份额
    Web高级 JavaScript中的算法
    Web高级 JavaScript中的数据结构
    JavaScript 集合对象
    Web高级 网站安全
    Web高级 HTTP报文
    Web高级 Eventloop和事件执行顺序
    Web高级 Ajax和跨域CORS
    javaScript 继承
    JavaScript 事件基础
  • 原文地址:https://www.cnblogs.com/zpfbuaa/p/5023363.html
Copyright © 2011-2022 走看看