zoukankan      html  css  js  c++  java
  • 一百层高楼和两个棋子

    这个很老的问题我很早就见到过了,答案当时也看了,只是感觉解答很强大,不曾多一点思考。直到上学期网友告诉我tencent的实习面试也考了这个问题,我才发现原来自己不曾真正的搞懂,遂就有了这篇文章(唠叨一下,2009春tencent的校园招聘曾考过远古时代的strlen递归,2009的暑期实习生又考这个问题,充分体现了tencent的创新精神)。

    问题表述:
    现有一百层高楼和两个棋子,棋子从X层上掉落摔到地面刚好摔碎(即X层以下是摔不碎的) 请问至少需要多少次摔棋子试验就一定能够找到X层是第几层? (棋子摔碎了就不能再用了)

    解答:
    假设第一次在第c1层测试;
    1)如果摔碎,那么在1..c1-1层只能一层一层测试;总共需要c1次
    2)如果没摔碎,那么在c1+(c1-1)层测试;
    3)如果摔碎,那么在c1+1..c1+(c1-1)层只能一层一层测试;总共需要c1次
    4)如果没摔碎,那么在c1+(c1-1)+(c1-2)层测试;
    。。。
    所以摔c1次可以检测到的总层数为:1+2+3+...+c1
    1+2+3+...+c1 >=100 ==> c1=14



    看看你能想到上述的方法吗?我是不行,只能逻辑思维了:

    假设第一颗在第 c1 层测试,有两种可能:
    1) 摔碎
    2) 没摔碎
    对于情况 1,说明 X 在 1 到 c1 之间,用递增法,c1 - 1 次可测得。加上第一次,共测试 c1 次。
    对于情况 2,说明 X > c1,于是进行第二轮。


    【第二轮】
    对于情况 2,用第一颗在第 c2 层测试(c2 > c1),有两种可能:
    2.1) 摔碎
    2.2) 没摔碎
    对于情况 2.1,说明 X 在 c1 + 1 到 c2 之间,用递增法,c2 - c1 - 1 次可测得。加上前两次,共测试 c2 - c1 + 1
    次。
    对于情况 2.2,说明 X > c2,进行第三轮。


    【第三轮】
    对于情况 2.2,用第一颗在第 c3 层测试(c3 > c2),仍有两种可能:
    2.2.1) 摔碎
    2.2.2) 没摔碎
    对于情况 2.2.1,说明 X 在 c2 + 1 到 c3 之间,用递增法,c3 - c2 - 1 次可测得。加上前三次,共测试 c3 - c2 + 2
    次。
    对于情况 2.2.2,说明 X > c3,进行第四轮。


    以此类推下去,直到第 n 轮 c(n) >= 总楼层数(即 100),这时候没摔碎的情况已经不会出现。测试结束。
    总测试次数为:
    max {
      c1,
      c2 - c1 + 1,
      c3 - c2 + 2,
      c4 - c3 + 3,
      ...
      c(n) - c(n-1) + (n-1)
    }
    为什么求这些值的最大值呢,因为只有这些值之中的最大值才能满足覆盖到100层楼的次数。什么意思?
    举个例子,看下面四个数字
    5
    4
    8
    6
    虽然最后一个是6,但是临界点如果在8,那么你6次就不能够测出这个位置,这也就是为什么取区间内的最大值了。

    题目中说,求最少多少次能够测出,在这里也就是说,如何让这个最大值最小化。
    观察这个数列(c1, c2 - c1 + 1, c3 - c2 + 2,….)你会发现前一项与后一项的关系,如果前一项特别大的话,就会是后一项特别小。那么为了使最大值比较小的话,就要让这些项都相等,于是
    c2 = 2 * c1 - 1
      c3 = c1 + c2 - 2 = 3 * c1 - 3
      c4 = c1 + c3 - 3 = 4 * c1 - 6
      ...
      c(n) = n * c1 - n(n-1)/2

    以上,即保证在 n 轮 c1 次测试中,必能穷尽 c(n) 层楼。
    现在,我们要求测试次数最少,也就是求 c1 的最小值(每一项都是c1的增函数,如果c1最小,函数也会最小)。


    因为 c(n) >= 100
    所以 n * c1 - n(n-1)/2 >= 100
    即 c1 >= 100/n + (n-1)/2 = 100/n + n/2 - 1/2 >= 2 * sqrt(100/n * n/2) - 1/2 =
    13.6
    (以上用到均值不等式)
    因此 c1 的最小值为 14,即最少需要测试 14 次。
    分别在 14, 27, 39, 50, 60, 69, 77, 84, 90, 95, 99, 100 层进行 12 轮测试。

    如果你感觉黄色部分你很难理解的话,这里由一个转化成数学题的解法,十分直观明了,就是不好想到
    其实楼主所贴的第一个解答是很漂亮的. 它已把本题化为如下极值问题:

    对任意自然数n, 以及满足c1 < c2 < ... < c(n-1) < c(n) = 100 的任意n个自然数c1,c2,...c(n), 定义

    多元函数
      f(n,c1,c2,...c(n)) =
      max {
          c1,
          c2 - c1 + 1,
          c3 - c2 + 2,
          c4 - c3 + 3,
          ...
          c(n) - c(n-1) + (n-1)
      }
    求f的极小值

    只是在后面求此极小值的过程让楼主有疑问.我在这里试着把求值过程更形式化一些,希望能解决楼主的疑问

    .

    由f的定义知:

       f >= c1
       f >= c2 - c1 + 1
       f >= c3 - c2 + 2,
       f >= c4 - c3 + 3,
       ...
       f >= c(n) - c(n-1) + (n-1)
      
    把上面n个式子相加,得:
       n*f >= c(n) + 1 + 2 + 3 + ... + (n-1)
            = 100 + 1 + 2 + 3 + ... + (n-1)
            = 100 + n(n-1)/2

    因此, f >= 100/n + (n-1)/2 = 100/n + n/2 - 1/2 >= 2 * sqrt(100/n * n/2) - 1/2 = 13.6
    因为f只能取整数,上式表明f的值是大于等于14的. 当然,仅据此还不足以说明f的极小值就是14, 因为我们

    还不知道f能否取到14. 这时候,用一个取到14的实例就完成了关于f的极小值为14的结论:

      分别在 14, 27, 39, 50, 60, 69, 77, 84, 90, 95, 99, 100 层进行测试.

    这么经典的问题,信息论当然不会放过,看一看信息论的解法:
    这题可以转化为信息论来解,两颗碎棋子,表示100种不同的状态,其中存在碎1颗和碎2颗的情况,求最短的编码长度。

    碎了为1,不碎为0,这样的话,100层楼就可以转化为100种不同的01编码,
    编码中最多允许有2个1,也可以只有一个1,比如14层碎了,然后从1试到13都没有碎,编码就是10000000000000。
    对应的是14楼。


    即:C(n,1) + C(n,2) >= 100,其中C(n,1)表示碎1颗的情况,C(n,2)表示碎2颗的情况。

    n + n*(n-1)/2 >= 100
    =>
    n(n+1) >= 200
    =>
    n >= 14

    用这种信息编码的方式考虑这道题,可以把问题扩展到m层楼,n个棋子,而不只局限于当前这道题。LZ可以试试1000层楼,
    3个棋子的情况,用这种编码方式可以很轻松的求得解
    对于信息论,咱知道的也不多,不过网上的资料不少,LZ可以自行搜索一下,
    有不少问题都可以用信息论求得一个下限,比如经典的12彩球问题,另外附带一大堆用天平秤球的问题都可以用信息论来解。
    再比如论坛里以前讨论过的一个赛马的问题。

    对于1000层,3颗棋子,第一次实验的层数,只需要求得

    c(n,3) + c(n,2) + c(n,1) >= 1000 其中的n就可以了,如果碎了,问题就转化为2颗棋子n层楼,
    如果没有碎,问题转化为3颗棋子1000-n层楼

  • 相关阅读:
    面向对象三 组合 特殊成员
    面向对象二 成员 嵌套
    面向对象
    模块和包
    异常处理
    os模块和序列化模块
    常用模块一
    常用模块 re模块
    内置函数二
    Algs4-2.2.14归并有序的队列
  • 原文地址:https://www.cnblogs.com/dmesg/p/1546901.html
Copyright © 2011-2022 走看看