0%

leetcode.887.鸡蛋掉落

鸡蛋掉落

问题描述


你将获得 K 个鸡蛋,并可以使用一栋从 1 到 N 共有 N 层楼的建筑。

每个蛋的功能都是一样的,如果一个蛋碎了,你就不能再把它掉下去。

你知道存在楼层 F ,满足 0 <= F <= N 任何从高于 F 的楼层落下的鸡蛋都会碎,从 F 楼层或比它低的楼层落下的鸡蛋都不会破。

每次移动,你可以取一个鸡蛋(如果你有完整的鸡蛋)并把它从任一楼层 X 扔下(满足 1 <= X <= N)。

你的目标是确切地知道 F 的值是多少。

无论 F 的初始值如何,你确定 F 的值的最小移动次数是多少?


动态规划

问题分析


f(i,j) i 代表鸡蛋个数,j代表楼层
f(i,j)=min(max(f(i,f-1) + 1,f(i-1,j-f)+1)) i<=f<=j

i\j 1 2 3 4 5 6 7 8 9
1 1 2 3 4 5 6 7 8 9
2 1 2 2 3 3 3 4 4 4
3 1 2 2 3 3 3 3 4 4
4 1 2 2 3 3 3 3 4 4
5 1 2 2 3 3 3 3 4 4
9 1 2 2 3 3 3 3 4 4

O(KNN)
超时


代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Solution {
public:
int dp[101][1001] = { 0 };
int superEggDrop(int K, int N) {
int i, j, f;
for (i = 1; i <= K; i++)
{
dp[i][1] = 1;
}
for (j = 1; j <= N; j++)
{
dp[1][j] = j;
}

int temp;
for (i = 2; i <= K; i++)
{
for (j = 2; j <= N; j++)
{
dp[i][j] = max(dp[i - 1][0] + 1, dp[i][j - 1] + 1);
for (f = 2; f <= j; f++)
{
temp = max(dp[i - 1][f - 1] + 1, dp[i][j - f] + 1);
dp[i][j] = min(dp[i][j], temp);
}
}
}

return dp[K][N];
}
};

动态规划+二分法

问题分析


max(dp[i - 1][fMedian - 1] + 1, dp[i][j - fMedian] + 1)函数是一个先递减在递增或者先递增再递减的函数,并且导数比较稳定

超时


代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
class Solution {
public:

int dp[101][10001] = {0};
int superEggDrop(int K, int N) {
int i, j;
for (i = 1; i <= K; i++)
{
dp[i][1] = 1;
}
for (j = 1; j <= N; j++)
{
dp[1][j] = j;
}
int temp;
int ff;
int fMin, fMax;
int fMedian;
int median,left, right;
int ii, jj;
for (i = 2; i <= K; i++)
{
for (j = 2; j <= N; j++)
{
fMin = 1; fMax = j;
while (fMin <= fMax)
{
fMedian = (fMin + fMax) / 2;
median = max(dp[i - 1][fMedian - 1] + 1, dp[i][j - fMedian] + 1);
if (fMedian == 1)
{
for (ii = fMedian + 1; ii <= j; ii++)
{
right = max(dp[i - 1][ii - 1] + 1, dp[i][j - ii] + 1);
if (right != median)
{
break;
}
}
if (median >= right)
{
fMin = fMedian + 1;
}
else
{
break;
}
}
else if (fMedian == j)
{
for (jj = fMedian - 1; jj >= 1; jj--)
{
left = max(dp[i - 1][jj - 1] + 1, dp[i][j - jj] + 1);
if (left != median)
{
break;
}
}
if (median >= left)
{
fMax = fMedian - 1;
}
else
{
break;
}
}
else
{
for (ii = fMedian + 1; ii <= j; ii++)
{
right = max(dp[i - 1][ii - 1] + 1, dp[i][j - ii] + 1);
if (right != median)
{
break;
}
}
if (median >= right)
{
fMin = fMedian + 1;
continue;
}
for (jj = fMedian - 1; jj >= 1; jj--)
{
left = max(dp[i - 1][jj - 1] + 1, dp[i][j - jj] + 1);
if (left != median)
{
break;
}
}
if (median >= left)
{
fMax = fMedian - 1;
continue;
}
break;
}
}
dp[i][j] = median;
}
}

return dp[K][N];
}
};

动态规划+二分法

问题分析



图片来源于leetcode

fMedian 的计算二分查找

  • 1.T1 = dp[i - 1][f - 1] 是单调递增函数
  • 2.T2 = dp[i][j - f] 是单调递减函数
  • 3.max(dp[i - 1][f - 1],dp[i][j - f]),就是两个函数的交点
  • 4.交点可能是浮点数,如果是整数,fmin=fmax=fmedian 如果是浮点数,离散化,求出交点位置的左右点

完美解决问题


代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
class Solution {
public:

int dp[101][10001] = {0};
int superEggDrop(int K, int N) {
int i, j;
for (i = 1; i <= K; i++)
{
dp[i][1] = 1;
}
for (j = 1; j <= N; j++)
{
dp[1][j] = j;
}
int temp;
int ff;
int fMin, fMax;
int fMedian;
int median,left, right;
int ii, jj;
for (i = 2; i <= K; i++)
{
for (j = 2; j <= N; j++)
{
fMin = 1; fMax = j;
while (fMin + 1 < fMax)
{
fMedian = (fMin + fMax) / 2;
left = dp[i - 1][fMedian - 1];
right = dp[i][j - fMedian];
if (left < right)
{
fMin = fMedian + 1;
}
else if (left > right)
{
fMax = fMedian - 1;
}
else
{
fMin = fMedian;
fMax = fMedian;
}
}
median = min(max(dp[i - 1][fMin - 1] + 1, dp[i][j - fMin] + 1)
, max(dp[i - 1][fMax - 1] + 1, dp[i][j - fMax] + 1));
dp[i][j] = median;
}
}

return dp[K][N];
}
};