一百层高楼和两个棋子

这个很老的问题我很早就见到过了,答案当时也看了,只是感觉解答很强大,不曾多一点思考。直到上学期网友告诉我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层楼

原文地址:https://www.cnblogs.com/dmesg/p/1546901.html