comments | difficulty | edit_url | tags | |||
---|---|---|---|---|---|---|
true |
困难 |
|
给你 k
枚相同的鸡蛋,并可以使用一栋从第 1
层到第 n
层共有 n
层楼的建筑。
已知存在楼层 f
,满足 0 <= f <= n
,任何从 高于 f
的楼层落下的鸡蛋都会碎,从 f
楼层或比它低的楼层落下的鸡蛋都不会破。
每次操作,你可以取一枚没有碎的鸡蛋并把它从任一楼层 x
扔下(满足 1 <= x <= n
)。如果鸡蛋碎了,你就不能再次使用它。如果某枚鸡蛋扔下后没有摔碎,则可以在之后的操作中 重复使用 这枚鸡蛋。
请你计算并返回要确定 f
确切的值 的 最小操作次数 是多少?
示例 1:
输入:k = 1, n = 2 输出:2 解释: 鸡蛋从 1 楼掉落。如果它碎了,肯定能得出 f = 0 。 否则,鸡蛋从 2 楼掉落。如果它碎了,肯定能得出 f = 1 。 如果它没碎,那么肯定能得出 f = 2 。 因此,在最坏的情况下我们需要移动 2 次以确定 f 是多少。
示例 2:
输入:k = 2, n = 6 输出:3
示例 3:
输入:k = 3, n = 14 输出:4
提示:
1 <= k <= 100
1 <= n <= 104
我们设计一个函数
函数
如果
如果
否则,我们考虑枚举第一个鸡蛋从第
如果按照这样的方式枚举,由于状态数有
我们注意到函数
时间复杂度
class Solution:
def superEggDrop(self, k: int, n: int) -> int:
@cache
def dfs(i: int, j: int) -> int:
if i < 1:
return 0
if j == 1:
return i
l, r = 1, i
while l < r:
mid = (l + r + 1) >> 1
a = dfs(mid - 1, j - 1)
b = dfs(i - mid, j)
if a <= b:
l = mid
else:
r = mid - 1
return max(dfs(l - 1, j - 1), dfs(i - l, j)) + 1
return dfs(n, k)
class Solution {
private int[][] f;
public int superEggDrop(int k, int n) {
f = new int[n + 1][k + 1];
return dfs(n, k);
}
private int dfs(int i, int j) {
if (i < 1) {
return 0;
}
if (j == 1) {
return i;
}
if (f[i][j] != 0) {
return f[i][j];
}
int l = 1, r = i;
while (l < r) {
int mid = (l + r + 1) >> 1;
int a = dfs(mid - 1, j - 1);
int b = dfs(i - mid, j);
if (a <= b) {
l = mid;
} else {
r = mid - 1;
}
}
return f[i][j] = Math.max(dfs(l - 1, j - 1), dfs(i - l, j)) + 1;
}
}
class Solution {
public:
int superEggDrop(int k, int n) {
int f[n + 1][k + 1];
memset(f, 0, sizeof(f));
auto dfs = [&](this auto&& dfs, int i, int j) -> int {
if (i < 1) {
return 0;
}
if (j == 1) {
return i;
}
if (f[i][j]) {
return f[i][j];
}
int l = 1, r = i;
while (l < r) {
int mid = (l + r + 1) >> 1;
int a = dfs(mid - 1, j - 1);
int b = dfs(i - mid, j);
if (a <= b) {
l = mid;
} else {
r = mid - 1;
}
}
return f[i][j] = max(dfs(l - 1, j - 1), dfs(i - l, j)) + 1;
};
return dfs(n, k);
}
};
func superEggDrop(k int, n int) int {
f := make([][]int, n+1)
for i := range f {
f[i] = make([]int, k+1)
}
var dfs func(i, j int) int
dfs = func(i, j int) int {
if i < 1 {
return 0
}
if j == 1 {
return i
}
if f[i][j] != 0 {
return f[i][j]
}
l, r := 1, i
for l < r {
mid := (l + r + 1) >> 1
a, b := dfs(mid-1, j-1), dfs(i-mid, j)
if a <= b {
l = mid
} else {
r = mid - 1
}
}
f[i][j] = max(dfs(l-1, j-1), dfs(i-l, j)) + 1
return f[i][j]
}
return dfs(n, k)
}
function superEggDrop(k: number, n: number): number {
const f: number[][] = new Array(n + 1).fill(0).map(() => new Array(k + 1).fill(0));
const dfs = (i: number, j: number): number => {
if (i < 1) {
return 0;
}
if (j === 1) {
return i;
}
if (f[i][j]) {
return f[i][j];
}
let l = 1;
let r = i;
while (l < r) {
const mid = (l + r + 1) >> 1;
const a = dfs(mid - 1, j - 1);
const b = dfs(i - mid, j);
if (a <= b) {
l = mid;
} else {
r = mid - 1;
}
}
return (f[i][j] = Math.max(dfs(l - 1, j - 1), dfs(i - l, j)) + 1);
};
return dfs(n, k);
}
我们也可以使用动态规划的方法解决这个问题。
我们定义
状态转移方程为
与方法一类似,我们可以使用二分查找来优化
时间复杂度
class Solution:
def superEggDrop(self, k: int, n: int) -> int:
f = [[0] * (k + 1) for _ in range(n + 1)]
for i in range(1, n + 1):
f[i][1] = i
for i in range(1, n + 1):
for j in range(2, k + 1):
l, r = 1, i
while l < r:
mid = (l + r + 1) >> 1
a, b = f[mid - 1][j - 1], f[i - mid][j]
if a <= b:
l = mid
else:
r = mid - 1
f[i][j] = max(f[l - 1][j - 1], f[i - l][j]) + 1
return f[n][k]
class Solution {
public int superEggDrop(int k, int n) {
int[][] f = new int[n + 1][k + 1];
for (int i = 1; i <= n; ++i) {
f[i][1] = i;
}
for (int i = 1; i <= n; ++i) {
for (int j = 2; j <= k; ++j) {
int l = 1, r = i;
while (l < r) {
int mid = (l + r + 1) >> 1;
int a = f[mid - 1][j - 1];
int b = f[i - mid][j];
if (a <= b) {
l = mid;
} else {
r = mid - 1;
}
}
f[i][j] = Math.max(f[l - 1][j - 1], f[i - l][j]) + 1;
}
}
return f[n][k];
}
}
class Solution {
public:
int superEggDrop(int k, int n) {
int f[n + 1][k + 1];
memset(f, 0, sizeof(f));
for (int i = 1; i <= n; ++i) {
f[i][1] = i;
}
for (int i = 1; i <= n; ++i) {
for (int j = 2; j <= k; ++j) {
int l = 1, r = i;
while (l < r) {
int mid = (l + r + 1) >> 1;
int a = f[mid - 1][j - 1];
int b = f[i - mid][j];
if (a <= b) {
l = mid;
} else {
r = mid - 1;
}
}
f[i][j] = max(f[l - 1][j - 1], f[i - l][j]) + 1;
}
}
return f[n][k];
}
};
func superEggDrop(k int, n int) int {
f := make([][]int, n+1)
for i := range f {
f[i] = make([]int, k+1)
}
for i := 1; i <= n; i++ {
f[i][1] = i
}
for i := 1; i <= n; i++ {
for j := 2; j <= k; j++ {
l, r := 1, i
for l < r {
mid := (l + r + 1) >> 1
a, b := f[mid-1][j-1], f[i-mid][j]
if a <= b {
l = mid
} else {
r = mid - 1
}
}
f[i][j] = max(f[l-1][j-1], f[i-l][j]) + 1
}
}
return f[n][k]
}
function superEggDrop(k: number, n: number): number {
const f: number[][] = new Array(n + 1).fill(0).map(() => new Array(k + 1).fill(0));
for (let i = 1; i <= n; ++i) {
f[i][1] = i;
}
for (let i = 1; i <= n; ++i) {
for (let j = 2; j <= k; ++j) {
let l = 1;
let r = i;
while (l < r) {
const mid = (l + r + 1) >> 1;
const a = f[mid - 1][j - 1];
const b = f[i - mid][j];
if (a <= b) {
l = mid;
} else {
r = mid - 1;
}
}
f[i][j] = Math.max(f[l - 1][j - 1], f[i - l][j]) + 1;
}
}
return f[n][k];
}