题意:用k
个蛋测试n
层楼的f
值,求最坏情况(所有蛋碎蛋不碎的情况中)的最小测试次数。f
值表示在f及以下层蛋扔下去都不会碎,之上都会碎。
一、一个蛋的情况
只能从1楼逐层往上尝试,结果为楼层数。
二、2个蛋的情况(谷歌面试题的形式)
以100
层楼为例:
将楼层分为10等分,1,10,20,30…100
A操作是测试1, 10, 20, 30… 100 扔下,蛋是否会碎掉
B操作是从x1,x2,x3…x9逐层测试蛋是否碎
当A操作不碎,一直进行A;
当A操作在碎掉,就在之前的9层逐层尝试;
故最坏情况是100层碎掉,逐层尝试91…99
共19
次(10次A,9次B操作)。
三、 无穷个蛋
蛋就不需要考虑了,结果就是楼层数的二分。
四、一般情况,n层楼,k个蛋
dp[n][k] : n层楼k个鸡蛋最坏情况能得到f值的最小尝试次数
由上面讨论知:
dp[1][k] = 1
dp[n][1] = n
对于1-N层楼的任意K点:
若K处鸡蛋碎了,则问题转化为dp[K-1][j-1]
;
若K处鸡蛋没碎,则转化为求dp[N-K][j]
.
因为要最坏情况,所以取最大值,再+1(K点的尝试次数)。
因为蛋碎不碎是不可控的,所以要取max,而K的点却是可以控制的(枚举到的位置是确定的),故取min。
由于K点是任意的,故要枚举K点位置。
时间复杂度O(n * n * k)
//O(n*n*k)
int superEggDrop(int k, int n) {
// dp[i][j] : i层楼j个鸡蛋最坏情况能得到f值的最小尝试次数
vector<vector<int>>dp(n+1, vector<int>(k+1, 0));
for(int j = 1; j <= k; ++j) dp[1][j] = 1;
for(int i = 1; i <= n; ++i) dp[i][1] = i;
for(int i = 2; i <= n; ++i) {
for(int j = 2; j <= k; ++j) {
dp[i][j] = dp[i][j-1];
for(int K = 1; K <= i; ++K) {
dp[i][j] = min(dp[i][j], max(dp[K-1][j-1], dp[i-K][j]) + 1);
}
}
}
return dp[n][k];
}
思路二:
dp[i][i] : j
个鸡蛋做i
次尝试最多能在dp[i][j]
层楼中得到f
有dp[i][j] = dp[i-1][j-1] + dp[i-1][j] + 1
(尝试一次之后(1)+ 尝试i-1次楼层下面最高的楼层+ 上面最高能达到的楼层)
时间复杂度O(n*k)
int superEggDrop(int k, int n) {
// dp[i][j] : j个鸡蛋做i次尝试最多能在dp[i][j]层楼中得到f
// dp[i][j] = dp[i-1][j-1] + dp[i-1][j] + 1
vector<vector<int>>dp(n+1, vector<int>(k+1, 0));
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= k; ++j) {
dp[i][j] = dp[i-1][j-1] + dp[i-1][j] + 1;
}
if(dp[i][k] >= n) return i;
}
return n;
}
Reference:
李永乐老师讲解双蛋问题
AcWing