问题描述:两个软硬程度一样但未知的鸡蛋,它们有可能都在一楼就摔碎,也可能从一百层楼摔下来没事。
有座100层的建筑,要你用这两个鸡蛋确定哪一层是鸡蛋可以安全落下的最高位置。可以摔碎两个鸡蛋。在最坏的情况下最少需要几次测试,才能得到摔碎鸡蛋的楼层?
问题分析:很容易想到第一个鸡蛋去50层往下扔,碎了就让第二个鸡蛋从1遍历到50得出鸡蛋会在第几层碎,如果没碎就还让第一个去砸75,碎了51到75,没碎砸88,…以此类推。但这样稍微偏离了题意,如果题目改成无限个鸡蛋那么就可以使用这种方法:二分法。也很容易推出结论num=log2N+1;
这时我们注意到题目的要求:在最坏的情况下最少需要几次测试,才能得到摔碎鸡蛋的楼层?
最坏的情况,最少要几次
这听起来很矛盾,但是这样理解:所谓最坏情况就是仍在同一层第一个鸡蛋碎或不碎所产生的解的最大值就是我们要的最坏情况。比如说我第一个鸡蛋扔在第10层碎了,那么解就在1到9之间最多要1+9=10次;如果没碎则答案肯定不在前10层,就把前10层减去变成了90层2个鸡蛋问题(实际问题中前面解决过的子问题),重复以上步骤。最后比较碎或不碎所用次数最多的那一个。那么最少的次数又是怎么回事,就是刚刚上面举例的扔在第10层,那么最先扔在20层呢,50层呢,答案数肯定又不太一样了,我们所找的解就是现扔在第几层会使答案数最少。那就把先扔在第k层的所有情况全都遍历一遍。我们发现在走第k层的时候如果碎了就变成了k-1层1蛋问题,没碎则变成了floor-k层2蛋问题。这样转化为之前解决过的问题就产生了递推式
temp = max(f[k - 1][j - 1], f[i - k][j]) + 1;取了最坏情况(其中k是第一个蛋砸的楼层数,j是鸡蛋数,i是楼层数)
之后
f[i][j] = min(temp, f[i][j]);最少次数
///1 2 3 4 5 6 7
1 1 2 3 4 5 6 7
2 1 2 2 3 3 3 4
当n层1蛋时我们只能从1层往上试那就是n层n次
当1层两蛋时我们当然需要1次,2层两蛋时也是两次,3层两蛋第一个蛋砸2层,碎:转换为1层1蛋,不碎:1层2蛋,结果就都可以转换为已解决问题得解
如当7层2蛋时砸到第k层时,这里我们假设k=4,如果碎了那么就变成了k-1即3层1蛋问题,这显然可以在我们上面的表里找到3,再加上第一个蛋砸的那一次是4次;如果没碎就成了7-k层2蛋即3层2蛋问题我们也可以在表里找到2+1=3次,最坏情况就是这两种情况中次数最多的那一种。而最少次数就是第一个蛋砸第几层是最好的。这里我个人认为是砸楼层中间那层会是最好的。
#include <bits/stdc++.h>
using namespace std;
int main()
{
const int FLOOR = 101;
const int EGG = 3;
int f[FLOOR][EGG];
for (int i = 0; i < FLOOR; ++i)
{
for (int j = 0; j < EGG; ++j)
{
f[i][j] = 0x7fffffff;
}
}
// i层楼,1个鸡 蛋
for (int i = 1; i < FLOOR; ++i)
f[i][1] = i;
//0,1层楼,i个鸡蛋
for (int i = 1; i < EGG; ++i)
{
f[0][i] = 0;
f[1][i] = 1;
}
for(int j = 2; j < EGG; ++j)
{
for (int i = 2; i < FLOOR; ++i)
{
// 尝试从1到i层楼扔下,在最坏的情况中选择最好的
for (int k = 1; k <= i; ++k)
{
int temp = max(f[k - 1][j - 1], f[i - k][j]) + 1;
f[i][j] = min(temp, f[i][j]);
//cout<<f[i][j]<<' ';
}
//cout<<endl;
}
}
cout << f[100][2] << endl;
return 0;
}