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