经典动态规划:高楼扔鸡蛋 :: labuladong的算法小抄 #1042
Replies: 11 comments 7 replies
-
递归+备忘录 lc上超时了 |
Beta Was this translation helpful? Give feedback.
-
备忘录+递归确实会超时
|
Beta Was this translation helpful? Give feedback.
-
1 |
Beta Was this translation helpful? Give feedback.
-
会超时,但是想往之前的套路里塞一塞 class Solution {
public:
vector<vector<int>> memo; // 备忘录
int superEggDrop(int k, int n) {
// 自底向上的DP
// 状态:鸡蛋,楼层
// 选择:碎没碎
memo = vector<vector<int>>(k+1, vector<int>(n+1));
// dp[i][j]表示:当前手握i个鸡蛋身处j层楼的最少扔鸡蛋次数是dp[k][j]。
vector<vector<int>> dp(k+1, vector<int>(n+1));
// base case
for(int i = 1; i < dp.size(); i++){
for(int j = 1; j < dp[0].size(); j++){
dp[i][j] = j;
}
}
// 穷举所有状态
// int res = INT_MAX;
for(int i = 2; i < dp.size(); i++){ // 鸡蛋
for(int j = 1; j < dp[0].size(); j++){ // 楼层
// 穷举选择:在第几层楼扔
// 碎了,去楼下
// 没碎,去楼上
for(int h = 1; h <= j; h++){
dp[i][j] = min(dp[i][j],
max(dp[i - 1][h - 1], dp[i][j-h]) + 1);
}
}
}
return dp[k][n];
}
}; |
Beta Was this translation helpful? Give feedback.
-
第二种dp定义+空间压缩 class Solution {
public int superEggDrop(int k, int n) {
// dp[i][j] 有i个鸡蛋,最多扔j次时,最坏情况下最多能测的最大楼层数
int[] dp = new int[k + 1];
int m = 0;
while(dp[k] < n) {
m++;
int pre = 0;
for(int i = 1; i <= k; i++) {
int tmp = dp[i];
// 最多m次的能测的楼层数 = 该层没碎 + 该层碎了 + 该层
dp[i] = dp[i] + pre + 1;
pre = tmp;
}
}
return m;
}
} |
Beta Was this translation helpful? Give feedback.
-
你好,为啥我分析感觉最后的O(K*logN)的算法不可行呢? int lo = 1,hi = n;
while (lo < hi) {
int mid = (lo + hi) / 2;
if (... < N){
// ①
}else{
//②
}
for i in k {
....
}
} 首先,①、②处一定是利用的dp[k][mid]来判断,此时使用的为dp数组,自底向上的而非自顶向下,并不能往下递归得出值。 |
Beta Was this translation helpful? Give feedback.
-
这个题目的一个很大的疑惑是:怎么判断鸡蛋碎没碎?题目没有任何提示。 |
Beta Was this translation helpful? Give feedback.
-
我现在不仅想知道最小操作数,还想知道它具体是怎么选择楼层进行试探的,该怎么写代码? |
Beta Was this translation helpful? Give feedback.
-
个人感觉 最坏情况就是楼层尽可能高, 最少次数就是用尝试次数最少来确定尽可能高的恰好不摔坏的楼层 |
Beta Was this translation helpful? Give feedback.
-
打卡,附上详细注解的Java代码 //下面写一下使用二分搜索优化的自顶向下的动态规划算法。
//使用动态规划必须先明确状态和选择 状态:可以尝试的鸡蛋次数,楼层数 选择:从哪个楼层扔鸡蛋
//因为是一个自顶向下的动态规划问题,这个问题中有重叠子问题,所以使用备忘录来消除重叠子问题
int[][] memo;
public int superEggDrop(int k, int n) {
//整体右移一位,从下标1开始更符合生活实际
memo = new int[k + 1][n + 1];
for (int[] ints : memo) {
Arrays.fill(ints, -1);
}
return dp(k, n);
}
//声明一下dp函数的定义:当有k个鸡蛋,n层楼的时候确定恰好不碎的楼层的最少尝试次数为dp(k,n)
private int dp(int k, int n) {
//base case
//1.当我们只有一个鸡蛋的时候,只能进行线性扫描才能确定最坏情况下的恰好不碎的楼层
if (k == 1) {
return n;
}
//2.当楼层为0或者鸡蛋为0的时候,尝试的次数肯定为0
if (k == 0 || n == 0) {
return 0;
}
if(memo[k][n]!=-1){
return memo[k][n];
}
//状态转移方程:如果此时碎了,那么楼层在当前楼层的下面,楼层数为i-1 如果没有随楼层在当前楼层上面,楼层数为n-i
// 因为是最坏情况下所以碎没碎取决于尝试的次数,选择尝试次数多的那个
int res = Integer.MAX_VALUE;
int low = 1, high = n;
while (low <= high) {
//这个mid其实就是使用二分搜索快速知道的中间的尝试楼层,也就是当前仍的那个楼层
int mid = (low + high) / 2;
//鸡蛋碎的情况
int broke = dp(k - 1, mid - 1);
//鸡蛋没有碎的情况
int not_broke = dp(k, n - mid);
//因为是最坏情况下,所以我们选择碎或者没碎中尝试次数最多,也就是最大的那个
if (broke > not_broke) {
//碎的情况下尝试的次数多于不碎
//下次我们应该去楼下尝试
high = mid - 1;
//因为最后结果是尝试次数最少,所以取最小值
res = Math.min(res, broke + 1);
} else {
//不碎的情况下尝试的次数多于碎
//下次我们应该去楼上尝试
low = mid + 1;
res = Math.min(res, not_broke + 1);
}
}
memo[k][n] = res;
return res;
} |
Beta Was this translation helpful? Give feedback.
-
为什么每次要在碎于不碎之前取最大值呢,这个不是很理解 |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
文章链接点这里:经典动态规划:高楼扔鸡蛋
评论礼仪 见这里,违者直接拉黑。
Beta Was this translation helpful? Give feedback.
All reactions