关于高楼扔鸡蛋问题,本文只对动态规划方法进行概括性的描述,具体看这里。
题目描述
K个鸡蛋 N 层楼,问至少尝试多少次可以找到鸡蛋不碎的临界楼层,求最少次数M
动态规划
方案1
设dp[K][N] 表示K个鸡蛋N层楼要尝试的最少次数
·
dp[K][N] = min 1 ≤ X ≤ N \min_{1\le X \le N} min1≤X≤N(max(dp[K-1][X-1], dp[K][N-X]))
dp[K-1][X-1]递增,dp[K][N-X]递减,dp[K][N]相当于取谷底,可以二分
方案2
设dp[K][M] 表示K个鸡蛋尝试M次可以确定的最大层数
·
dp[K][M] = dp[K][M-1] + dp[K-1][M-1] + 1
dp[K][M-1] :鸡蛋没碎,楼下K个蛋尝试M-1次可以确定的最大层数
dp[K-1][M-1] :鸡蛋碎了,楼上K-1个蛋尝试M-1次可以确定的最大层数
·
可以进一步压缩空间,因为本次答案只和上次的M-1有关,可以压缩成一维的
dp[K] = dp[K] + dp[K-1] + 1
vector<vector<int>> dp(K+1, vector<int>(N+1, 0));
int m = 0;
while(dp[K] < N){
m ++;
for(int k=1; k<=K; k++)
dp[k] = dp[k] + dp[k-1] + 1;
}
return m;
数学方法
观察上述方案2的方程,为了下面方便表示,换一下表示形式f(M,K) = dp[K][M]
,
设g(m,k) = f(m,k)-f(m,k-1)
,则:
g
(
m
,
k
)
=
f
(
M
,
K
)
−
f
(
M
,
K
−
1
)
=
f
(
M
−
1
,
K
)
+
f
(
M
−
1
,
K
−
1
)
+
1
−
(
f
(
M
−
1
,
K
−
1
)
+
f
(
M
−
1
,
K
−
2
)
+
1
)
=
g
(
m
−
1
,
k
)
+
g
(
m
−
1
,
k
−
1
)
\begin{aligned} g(m,k) &= f(M,K)-f(M,K-1) \\ &= f(M-1,K) + f(M-1,K-1)+1-(f(M-1,K-1) + f(M-1,K-2)+1) \\ &= g(m-1,k)+g(m-1,k-1) \end{aligned}
g(m,k)=f(M,K)−f(M,K−1)=f(M−1,K)+f(M−1,K−1)+1−(f(M−1,K−1)+f(M−1,K−2)+1)=g(m−1,k)+g(m−1,k−1)
??? 是否有些许熟悉,杨辉三角???
用 A r , c A_{r,c} Ar,c 表示杨辉三角中第 r 行第 c 列的元素,它的值又是二项式系数 C r c C_{r}^{c} Crc ,即: A r , c = A r − 1 , c + A r − 1 , c − 1 = C r c A_{r,c} = A_{r-1,c} + A_{r-1,c-1} = C_{r}^{c} Ar,c=Ar−1,c+Ar−1,c−1=Crc
所以 :
g
(
m
,
k
)
=
C
m
k
+
1
f
(
m
,
k
)
=
∑
1
≤
x
≤
K
g
(
m
,
x
)
=
∑
C
m
x
\begin{aligned} g(m,k) &= C_m^{k+1} \\ f(m,k) &= \sum_{1 \le x \le K}g(m,x) = \sum C_{m}^{x} \end{aligned}
g(m,k)f(m,k)=Cmk+1=1≤x≤K∑g(m,x)=∑Cmx
从另一种角度理解这个公式:
k个蛋尝试m次,每次选楼层的的结果就是碎或不碎(1/0),所以不管选那一层,最后的方案就是一个长度为m的01序列,碎了0次的方案数就是 C m 0 C_m^0 Cm0,碎了一次的就是 C m 1 C_m^1 Cm1……,总方案数就是 ∑ 1 ≤ x ≤ K C m x \sum_{1\le x \le K}C_m^x ∑1≤x≤KCmx
现在的目标转化为找最小的 m 使得 f ( m , K ) ≥ N f(m,K) \ge N f(m,K)≥N ,可以二分寻找 m
class Solution {
public int superEggDrop(int K, int N) {
int l = 1, r = N;
while (l < r) {
int m = l + (r - l >> 1);
if (f(m, K, N) < N) l = m + 1;
else r = m;
}
return l;
}
public int f(int x, int K, int N) {
int ans = 0, r = 1;
for (int i = 1; i <= K; ++i) {
r *= x-i+1;
r /= i;
ans += r;
if (ans >= N) break;
}
return ans;
}
}
参考:
https://leetcode.com/articles/super-egg-drop/
https://zhuanlan.zhihu.com/p/92288604
https://zhuanlan.zhihu.com/p/95172244