上一主题 下一主题
ScriptCat,新一代的脚本管理器脚本站,与全世界分享你的用户脚本油猴脚本开发指南教程目录
返回列表 发新帖
楼主: 李恒道 - 

鸡蛋掉落问题分析

[复制链接]
  • TA的每日心情
    慵懒
    4 小时前
  • 签到天数: 193 天

    [LV.7]常住居民III

    710

    主题

    5872

    回帖

    6698

    积分

    管理员

    非物质文化遗产社会摇传承人

    积分
    6698

    荣誉开发者管理员油中2周年生态建设者喜迎中秋

    发表于 2024-10-15 01:01:45 | 显示全部楼层 | 阅读模式

    https://leetcode.cn/problems/super-egg-drop/description/

    第一步我们其实很容易分析出来递归
    首先拥有K个鸡蛋,可能N层楼
    第一个鸡蛋可能在任意一层为最优
    则遍历1...N
    每层都尝试落鸡蛋,取所有的最小操作次数
    而落鸡蛋有两种可能,假设楼层在T
    一种是蛋碎了,则问题转化为在K-1个蛋在1..T-1层放鸡蛋的问题
    一种是蛋没碎,则问题转化为K个蛋在T+1到N层放鸡蛋的问题
    很容易得到递推公式
    取最大(dp[鸡蛋 - 1][当前楼层 - 1], dp[鸡蛋][最高楼层 - 当前楼层]) + 1

    那我们可以很容易写出来dp代码

    var superEggDrop = function (k, n) {
        // k蛋数量
        const dp = new Array(k + 1).fill(0).map(() => new Array(n + 1).fill(Number.MAX_SAFE_INTEGER))
        for (let index = 0; index < dp[0].length; index++) {
            dp[1][index] = index
        }
        for (let index = 0; index < dp.length; index++) {
            dp[index][0] = 0;
        }
        for (let index = 2; index < dp.length; index++) {
            for (let indey = 1; indey < dp[0].length; indey++) {
                const layoutHeight = indey;
                for (let currentLayout = 1; currentLayout <= layoutHeight; currentLayout++) {
                    dp[index][indey] = Math.min(dp[index][indey], Math.max(dp[index - 1][currentLayout-1], dp[index][layoutHeight - currentLayout])+1)
                }
            }
        }
        return dp.pop().pop()
    };

    是该代码复杂度为(K*N方)
    实际跑下来虽然结果正确会超时,这步我就没思路了
    看了labuladong大佬的解
    https://leetcode.cn/problems/super-egg-drop/solutions/44427/ji-ben-dong-tai-gui-hua-jie-fa-by-labuladong/

    其中可以发现dp[鸡蛋 - 1][当前楼层 - 1],随着遍历的楼层逐渐增加,结果也会增大
    dp[鸡蛋][最高楼层 - 当前楼层])随着遍历楼层的逐渐增加,最高楼层 - 当前楼层会变得越来越小,结果也会减小
    二者之间取最大,则一定存在一个沟壑
    可以采用二分法进行获取,复杂度就变成了(KNLogN)
    则单纯替换遍历为二分既可以通过

    var superEggDrop = function (k, n) {
        // k蛋数量
        const dp = new Array(k + 1).fill(0).map(() => new Array(n + 1).fill(Number.MAX_SAFE_INTEGER))
        for (let index = 0; index < dp[0].length; index++) {
            dp[1][index] = index
        }
        for (let index = 0; index < dp.length; index++) {
            dp[index][0] = 0;
        }
        for (let index = 2; index < dp.length; index++) {
            for (let indey = 1; indey < dp[0].length; indey++) {
                const layoutHeight = indey;
                let l = 0, r = layoutHeight;
                while (l <= r) {
                    const center = Math.floor((l + r) / 2);
                    let result1 = dp[index - 1][center - 1] ?? Number.MIN_SAFE_INTEGER //随着楼层增高逐渐增大
                    let result2 = dp[index][layoutHeight - center] //随着楼层增高逐渐变小
                    dp[index][indey] = Math.min(dp[index][indey], Math.max(result1, result2) + 1);
                    if (result1 <= result2) {
                        l = center + 1
                    } else {
                        r = center - 1
                    }
                }
            }
        }
        return dp.pop().pop()
    };

    这时候如果转换dp思维就可以得到一种更简单的解法
    假设有k个蛋,但是只有m次机会,那一共能覆盖多少楼层?
    可以分两种情况来进行讨论
    假设现在有一个高无限层,低无限层的大楼
    有k个蛋,m次机会,此时在某个点砸一颗蛋
    那蛋有两种可能,一种碎了,此时向下有多少层可以覆盖到
    一种是没有碎,此时向上有多少层可以覆盖到
    两种可能相加到一起的总楼层数再加上本层就可以得到k个蛋m次机会能覆盖到多少的楼层
    代码直接参考labuladong的就可以了
    不是特别复杂就不再写了

    混的人。
    ------------------------------------------
    進撃!永遠の帝国の破壊虎---李恒道

    入驻了爱发电https://afdian.net/a/lihengdao666
    个人宣言:この世界で私に胜てる人とコードはまだ生まれていません。死ぬのが怖くなければ来てください。

    发表回复

    本版积分规则

    快速回复 返回顶部 返回列表