谷歌当年的面试题的加强版,仿佛脑筋急转弯
一步一步来,要求的是最小的 最大步数。最大是指,在最坏情况下,求得F需要的步数;最小是指,在各种策略下,所能求出的最小的最大值。
优秀的dp走起来,那最优值即最小的最大步数,状态dp[n][k]表示n层k蛋的最优值
接下来,求dp[n][k]时,假设一个鸡蛋从第i层扔下,有两种情况:
- 碎了。碎了不能再用。那接下来测i之下的层数,即dp[n][k] = 1+dp[i-1][k-1]
- 没碎。没碎还能再用。接下来测的是i之上得到层数,即dp[n][k] = 1+dp[n-i][k]
之后我们再遍历每个i,求出最小的
状态转移方程为dp[n][k] = min(max(dp[n-i][k], dp[i-1][k-1]))
int superEggDrop(int K, int N) {
int dp[N+2][K+2];
for (int i = 0; i <= N; i ++) {
dp[i][0] = 0;
dp[i][1] = i;
}
for (int i = 0; i <= K; i ++) {
dp[0][i] = 0;
}
for (int k = 2; k <= K; k ++) {
for (int n = 1; n <= N; n ++) {
dp[n][k] = n;
for (int i = 1; i < n; i ++) {
int rst = max(dp[n-i][k], dp[i-1][k-1]) + 1;
if (rst < dp[n][k]) dp[n][k] = rst;
}
}
}
return dp[N][K];
}
时间复杂度为O(KN^2)
好了。没过。
求min(max(dp[n-i][k], dp[i-1][k-1]))时是对i遍历,找出最小的值,观察到dp[n-i][k]随i单调递减,dp[i-1][k-1]随i单调递增,最优的i值就是两条线的交点,可以用二分求解。
int superEggDrop(int K, int N) {
int dp[N+2][K+2];
for (int i = 0; i <= N; i ++) {
dp[i][0] = 0;
dp[i][1] = i;
}
for (int i = 0; i <= K; i ++) {
dp[0][i] = 0;
}
for (int k = 2; k <= K; k ++) {
for (int n = 1; n <= N; n ++) {
int il = 1, ih = n;
while (il+1 < ih) {
int mid = (il+ih)/2;
if (dp[n-mid][k] > dp[mid-1][k-1]) il = mid;
else if (dp[n-mid][k] < dp[mid-1][k-1]) ih = mid;
else il = ih = mid;
}
if (il == ih) dp[n][k] = max(dp[n-ih][k], dp[ih-1][k-1]) + 1;
else {
int ansl = max(dp[n-il][k], dp[il-1][k-1]);
int ansh = max(dp[n-ih+1][k], dp[ih-2][k-1]);
if (ansl < ansh) dp[n][k] = ansl + 1;
else dp[n][k] = ansh + 1;
}
}
}
return dp[N][K];
}
时间复杂度为O(KNlog(N))
其实,最优的 i 是随着 n 单调递增的。对每个k,只要保持最优的 i 随 s 单调增长就行。
int superEggDrop(int K, int N) {
int dp[N+2][K+2];
for (int i = 0; i <= N; i ++) {
dp[i][0] = 0;
dp[i][1] = i;
}
for (int i = 0; i <= K; i ++) {
dp[0][i] = 0;
}
for (int k = 2; k <= K; k ++) {
int i = 1;
for (int n = 1; n <= N; n ++) {
while (i < n && dp[n-i][k] > dp[i-1][k-1]) i ++;
dp[n][k] = max(dp[n-i][k], dp[i-1][k-1]) + 1;
}
}
return dp[N][K];
}
时间复杂度为O(KN)
还有一种特别绕,但特别简洁的算法,想出来它的人智商可能得两百多。
没看懂。