高楼扔玻璃球问题
题目描述:
腾讯大厦有100层,你手里有两颗玻璃球。当你拿着玻璃球在某一层往下扔的时候,一定会有两个结果,玻璃球碎了或者没碎。
大厦有个临界楼层。低于它的楼层,往下扔玻璃球,玻璃球不会碎,等于或高于它的楼层,扔下玻璃球,玻璃球一定会碎。玻璃球碎了就不能再扔。
现在让你设计一种方式,使得在该方式下,最坏的情况扔的次数比其他任何方式最坏的次数都少。也就是设计一种最有效的方式。
两种思路解法:
分区思想:
♦玻璃球属于消耗品,一旦第一颗球摔碎了,那么就只会剩下最后一颗球来去测量临界值,假设我们从第一层开始扔,那么最坏情况是从第一层试验到九十九层(最后一层就不用在测了),那么也需要试验99次
次,因此我们不应该从第一层开始试验
♦假设我们从任意一层楼x层将玻璃球扔下去,球分碎和不碎两种情况,如果碎了,那么就从该层楼继续往上扔,最坏需要(100-x-1(最后一层楼不用测临界值就是它了)+1(第一颗玻璃球碎的那一次))次,
如果没碎的话,那么继续把第一颗球从比x楼层高的y层扔,又分碎和没碎两种情况,知道第一颗球碎了,再讲第二颗球从上一次扔第一颗球没碎的楼层开始,逐层往上扔,从而找到临界值
因此x,y的划分仍然需要我们讲究区域的划分
♦现在我们自己进行区间划分,假设我们将区建设为10,那么1到100层就需要划分10个区间,从最底层的区间开始逐个往上试验(每次都试该区间的最高层)一旦球在
哪一个区间碎了,那么就可以锁定临界值在此区间,因此最坏情况为9+9(前九个区间+最后一个区间的前九层),
但是这样做的话,仍然不是很合理?因为按照概率来算:越往高层,球碎的概率就越大,也就是说前面区间划分的不是很均匀,越往高层时,高层区间大小应该越来越小,而底层区间由于球碎的可能性
较小,所以应该尽可能让区间大小变大,这样最终的结果:区间个数+最后一层区间的大小的值就会尽可能的变小
我们采取逐步递减的方式,让最后一个区间只有一层,那么倒数第二个区间就只有两层,就转变成了1+2+...+x=100,解得:x约等于14,1+...+14=105,因此最大实验次数不应该超过14
♦以此类推,若楼层为n层,那么问题就转变为从1+...+x=n,解得的x即为最坏的次数
动态规划思想:
class Main{ public static void main(String[] args) { int building = 100; //初始化,考虑数组边界0 int[] dp = new int[building+1]; //根据上面的思路,假设每一层都有碎的可能性,遍历求解,假设第i层球碎 for (int i = 2;i <=building;i++){ //最坏次数里面最优解法,假设为building,当然building-1更贴切,但并不影响程序结果 int Min = building;//每一次i层碎的情况引发后续的 最小次数不同,需要重置Min的次数 //第二层遍历,假设在第j层球碎 for (int j = 1;j < i;j++){ //那么用到的最大次数,要么从i层开始从上往下数到j层(i-j)+1(原来的第一次在i层碎的那一次), // 要么从第1层开始到j层,即用了j-1+1=j次 int Max = dp[i-j]+1; //因为要考虑最坏情况,所以要取最大值 if (Max < j){ Max = j; } //Min当然要取最小值了 if (Max < Min){ Min = Max; } } //第一次在i层碎的之后,后续个种情况的最小次数 dp[i] = Min; } //遍历各种在i层碎的可能情况 System.out.println(dp[building]); } }