一、题目描述
You are given K
eggs, and you have access to a building with N
floors from 1
to N
.
Each egg is identical in function, and if an egg breaks, you cannot drop it again.
You know that there exists a floor F
with 0 <= F <= N
such that any egg dropped at a floor higher than F
will break, and any egg dropped at or below floor F
will not break.
Each move, you may take an egg (if you have an unbroken one) and drop it from any floor X
(with 1 <= X <= N
).
Your goal is to know with certainty what the value of F
is.
What is the minimum number of moves that you need to know with certainty what F
is, regardless of the initial value of F
?
Example 1:
Input: K = 1, N = 2
Output: 2
Explanation:
Drop the egg from floor 1. If it breaks, we know with certainty that F = 0.
Otherwise, drop the egg from floor 2. If it breaks, we know with certainty that F = 1.
If it didn’t break, then we know with certainty F = 2.
Hence, we needed 2 moves in the worst case to know what F is with certainty.
Example 2:
Input: K = 2, N = 6
Output: 3
Example 3:
Input: K = 3, N = 14
Output: 4
二、题目分析
依题意,我们要求为了确定使鸡蛋从该层掉落也不会摔碎的最大的楼层数所需的最小操作数,无论该层数是多少(即在最坏的情况下至少要投下几次鸡蛋才能确定)。
-
失败尝试:
其实比较容易能想到的动态规划的方式是,在楼层数为N
并且拥有K
个鸡蛋时,记为条件 ( K , N ) ( K, N ) (K,N),我们在某一楼层f
投下鸡蛋看它是否摔碎——此时以f
层为分界线我们将楼层分为两部分: ( 0 , f − 1 ] ( 0, f-1 ] (0,f−1] 和 [ f , N ] [ f, N ] [f,N] ——若鸡蛋摔碎了,我们就在前半部分以条件 ( K − 1 , f − 1 ) ( K-1, f-1 ) (K−1,f−1) 找出所需楼层,否则在后半部分以条件 ( K , N − f ) ( K, N-f ) (K,N−f) 找出所需楼层,取这两者中找出所需楼层的操作数的最大值(最坏情况)作为从f
层投下鸡蛋确认一次之后至少所需的操作数。于是我们令 f = 1 , 2 , . . . , N f = 1, 2, ..., N f=1,2,...,N ,求出所有的最坏情况,取它们之中的最小值即可找到答案。令 S ( K , N ) S( K, N ) S(K,N)表示使用
K
个鸡蛋确定N
层楼的最终结果,则
S ( K , N ) = m i n ( 1 + m a x ( S ( K − 1 , f − 1 ) , S ( K , N − f ) ) ) , f = 1 , 2 , . . . , N S( K, N )= min( 1 + max( S( K-1, f-1 ), S( K, N-f) ) ), f = 1, 2, ..., N S(K,N)=min(1+max(S(K−1,f−1),S(K,N−f))),f=1,2,...,N通过自底向上的动态规划来实现便可求得解。但不幸的是这样做复杂度太高了,为 O ( K N 2 ) O(KN^2) O(KN2),在测试的时候超时了,所以这样子是行不通的。
-
正解:
在求解该题时,我们需要换个思路,不是求解最终的操作数,而是在给定操作数的情况下我们能确定到多少层楼,当该楼层数大于等于N
的时候,我们就求得了答案。这样处理的话这道题就变得异常简洁明了,上面那些冗长的分析也并不需要了——令 F ( K , S ) F( K, S ) F(K,S) 表示使用K
个鸡蛋在S
步下能够确认的最大的楼层数,这些楼层应该这样 构成 :F ( K , S ) = F ( K − 1 , S − 1 ) + 1 + F ( K , S − 1 ) F( K, S ) = F( K-1, S-1 ) + 1 + F( K, S-1 ) F(K,S)=F(K−1,S−1)+1+F(K,S−1)
其中, 1 1 1 代表我们投下一次鸡蛋的层数,该层被确认了。而投下的鸡蛋会有两种情况:
- 鸡蛋破碎,则我们少了一个鸡蛋,操作数也减一,但此时能够知道我们接下来应在 [ 1 , F ( K − 1 , S − 1 ) ] [ 1, F( K-1, S-1 ) ] [1,F(K−1,S−1)] 中确认目标楼层。
- 鸡蛋没有破碎,只有操作数减一,此时能够知道我们接下来要在 [ F ( K , S − 1 ) , F ( K , S ) ] [ F( K, S-1 ), F( K, S ) ] [F(K,S−1),F(K,S)] 中确认目标楼层。
现在我们就能使用动态规划自底向上构建解了。
三、具体实现
在通过鸡蛋数进行遍历时,可以改变顺序,变为从大到小遍历,便能使用一维数组来存储数据,节约空间。
class Solution
{
public:
int superEggDrop( int K, int N )
{
vector<int> floors( K + 1, 0 );
int step = 0;
for ( /**/; floors[K] < N; ++step ) {
for ( int i = K; i >= 1; --i ) {
// floors[i] means the number of the floors
// which can be checked in 'step+1' moves using i eggs
floors[i] = floors[i-1] + 1 + floors[i];
}
}
return step;
}
};