之前 dp[K,N]=m 表示的含义:鸡蛋有K个,楼最高N层,那么最坏情况下找到鸡蛋恰好不被摔破的楼层F的最小尝试次数为m
现在重新定义一下:
dp[K,m] = N : 鸡蛋有K个,最多可以扔m次,可以得到的最高楼层数N。
其实这个就是上面的反推。
完整代码如下
public int solution(int K, int N) {
int[][] dp = new int[K + 1][N + 1];
int m = 1;
for (; dp[K][m-1] < N; m++) {
for (int k = 1; k <= K; k++) {
dp[k][m] =dp[k][m-1]+ dp[k-1][m-1] + 1;
}
}
return m-1;
}
问题1
对于dp[K,m]来说,第一个鸡蛋哟啊先从第几层楼开始扔呢?为什么?
先从dp[K-1][m-1] +1 层扔。因为这种办法是上面的反推,所以我们先看dp[K,N]扔的时候会先从第几层扔。
比如 K=2,N=6,此时dp[2][6] = 3,扔的时候是先从第3层扔。整个楼层被分成了三组:
- 1,2
- 3
- 4,5,6
那么K=2,m=3,此时dp[K-1][m-1] = dp[1][2] + 1=3, 也是从第3层扔。
再用反证法证明一下:
-
更低楼层丢鸡蛋。最终得到的并不是“最大”楼层数,未充分挖掘鸡蛋数和移动次数的潜力,最终求解时会剩余一定量的鸡蛋或移动次数。比如上面的例子,如果是从第2层开始扔,那么就会分成这3层。
- 1 - 2 - 3,4,5,6 这样就会导致为充分挖掘鸡蛋数和移动次数的潜力
-
更高楼层丢鸡蛋。不妨假设高了一层,即在第dp[k-1][m-1]+2层丢鸡蛋。若蛋碎,则可排除该层以上的所有楼层;若蛋未碎,因为剩下的k-1个鸡蛋在m-1次移动只能求出dp[k-1][m-1]的楼层,而现在剩余的楼层却是dp[k-1][m-1]+1,得出矛盾!
问题2: 对于第一个鸡蛋状态的讨论
- 蛋碎:可用k-1个鸡蛋用m-1步测出下面的dp[k-1][m-1]层楼,此时,总共可以测出无限∞的楼层。(如果蛋碎,那么无论上面还有多少层,都是这个结果,所以可以测出无限层 )
- 蛋未碎:说明所求楼层数不在下面的dp[k-1][m-1]层楼中;此时,还剩k个鸡蛋和m-1次移动,可用dp[k-1][m-1]+1以上的楼层继续测试;总共可以测出dp[k-1][m-1]+dp[k][m-1]+1层楼。
step3: 得出最多可测的楼层数:
dp[k][m] = Math.min(dp[k-1][m-1]+dp[k][m-1]+1, ∞),即,
dp[k][m] = dp[k-1][m-1]+dp[k][m-1]+1
另一种理解方式
理解1
类似上面的图,代入数字理解一下
如果K=2,m=2,dp[2][2] = 3
扔第一个鸡蛋,如果碎了,dp[1,1]:就算碎了,我还有1个鸡蛋,和1次尝试机会,我可以看看此时我还能得到的最高层是多少
如果不碎,dp[2,1]:没碎,那我就还有2个鸡蛋,1次尝试机会,我可以看看此时我还能得到的最高层是多少。
最后结果一定是他俩的和。
理解2
对于dp[K][N] ,当K=2,N=6,会把整个搜索域分成3份
1,2
3
4,5,6
分的方式是蛋碎或者不碎。
那么dp[K][m],当K=2,m=3 要做的就是把这三个搜索区域合并起来。所以也同样是用蛋碎和蛋不碎来合并。
所以dp[k][m] = dp[k-1][m-1] + dp[k][m-1] + 1。
不管你蛋碎还是不碎,我要的是你们统共加起来的区域有多大。