动态规划 | 鸡蛋问题 | 元旦假期来点“蛋”题

news2025/1/26 15:53:55

文章目录

    • 鸡蛋掉落 - 两枚鸡蛋
      • 题目描述
      • 动态规划解法
        • 问题分析
        • 程序代码
    • 鸡蛋掉落
      • 题目描述
      • 问题分析
      • 程序代码
      • 复杂度分析

鸡蛋掉落 - 两枚鸡蛋

题目描述

原题链接

给你 2 枚相同 的鸡蛋,和一栋从第 1 层到第 n 层共有 n 层楼的建筑。

已知存在楼层 f ,满足 0 <= f <= n ,任何从 高于 f 的楼层落下的鸡蛋都 会碎 ,从 f 楼层或比它低 的楼层落下的鸡蛋都 不会碎

每次操作,你可以取一枚 没有碎 的鸡蛋并把它从任一楼层 x 扔下(满足 1 <= x <= n)。如果鸡蛋碎了,你就不能再次使用它。如果某枚鸡蛋扔下后没有摔碎,则可以在之后的操作中 重复使用 这枚鸡蛋。

请你计算并返回要确定 f 确切的值最小操作次数 是多少?

动态规划解法

问题分析

状态定义dp[i][j]表示总共有 i 层楼,现在手上有 j + 1 个鸡蛋。

状态计算dp[i][1] = min(dp[i][1], max(j, dp[i - j][1] + 1))

我们假设总共有 i 层楼,从第 j 层楼往下扔第一个鸡蛋,有两种情况:

  1. 鸡蛋碎了,那么说明f楼层一定小于j,即在第 j 层的楼下。此时的最少操作次数为j - 1 + 1 = j
  2. 鸡蛋没碎,那么说明f楼层一定大于j,即在第 j 层的楼上。接下来,我们仍然持有 2 个鸡蛋,但此时考虑的楼层数只有i - j层。此时最少操作次数为dp[i - j][1] + 1

最终从第 j 层往下扔第一个鸡蛋,所需的最少操作次数为max(j, dp[i - j][1] + 1)

我们要做的就是遍历所有可能的情况j,找到所需操作次数最小的情况。

初始化dp[i][0] = dp[i][1] = i

  • 如果手上只有 1 个鸡蛋,i 层楼至少需要操作 i 次。
  • 如果手上有 2 个鸡蛋,i 层楼的最少操作次数不超过 i 次。
程序代码
class Solution {
public:
    int twoEggDrop(int n) {
        vector<vector<int>> dp(n + 1, vector<int>(2, 0));
        // 初始化
        for(int i = 1; i <= n; i++) {
            dp[i][0] = i;
            dp[i][1] = i;
        }
        for(int i = 2; i <= n; i++) {
            for(int j = 1; j < i; j++) {
                dp[i][1] = min(dp[i][1], max(j, dp[i - j][1] + 1));
            }
        }
        return dp[n][1];
    }
};

观察上述代码,可以发现代码可以压缩成一维:

class Solution {
public:
    int twoEggDrop(int n) {
        vector<int> dp(n + 1);
        // 初始化
        for(int i = 1; i <= n; i++) {
            dp[i] = i;
        }
        for(int i = 2; i <= n; i++) {
            for(int j = 1; j < i; j++) {
                dp[i] = min(dp[i], max(j, dp[i - j] + 1));
            }
        }
        return dp[n];
    }
};

鸡蛋掉落

题目描述

原题链接

给你 k 枚相同的鸡蛋,并可以使用一栋从第 1 层到第 n 层共有 n 层楼的建筑。

已知存在楼层 f ,满足 0 <= f <= n ,任何从 高于 f 的楼层落下的鸡蛋都会碎,从 f 楼层或比它低的楼层落下的鸡蛋都不会破。

每次操作,你可以取一枚没有碎的鸡蛋并把它从任一楼层 x 扔下(满足 1 <= x <= n)。如果鸡蛋碎了,你就不能再次使用它。如果某枚鸡蛋扔下后没有摔碎,则可以在之后的操作中 重复使用 这枚鸡蛋。

请你计算并返回要确定 f 确切的值最小操作次数 是多少?

问题分析

如果套用上一题的分析思路,我们可以定义如下状态以及状态计算。

状态定义dp[i][j]表示总共有 i 层楼,现在手上有 j + 1 个鸡蛋。

状态计算dp[i][k] = min(dp[i][k], max(dp[j-1][k-1] + 1, dp[i - j][k] + 1)),其中k表示手上有k + 1个鸡蛋,从第 j 层开始扔鸡蛋。

但是该方法最终会 TLE

我们观察上述的状态转移方程,若我们固定鸡蛋的个数k + 1,可以发现,随着楼层数i的增加,dp[j-1][k-1] + 1这一项不会发生变动,即从第 j 层丢下的鸡蛋碎了。而dp[i - j][k] + 1这一项会随着楼层数i的增加而增加,即从第 j 层丢下的鸡蛋没碎。

接下来我们观察从第 j 层开始丢鸡蛋,随着j的增加,dp[j-1][k-1] + 1会逐渐增加,而dp[i - j][k] + 1会逐渐减小。而二者的交点位置就是dp[i][k]的最小值。

随着楼层数i的不断增加,dp[i - j][k] + 1不断上移动,而二者的交点也不断向右上方移动。

在这里插入图片描述

因此,当我们固定鸡蛋个数k+1时,随着楼层数i的不断增加,dp[i][j]最优解j的坐标也单调递增。

程序代码

class Solution {
public:
    int superEggDrop(int k, int n) {
        vector<int> dp(n + 1);
        // 初始化
        for(int i = 1; i <= n; i++) {
            dp[i] = i;
        }
        // 先固定鸡蛋个数
        for(int j = 2; j <= k; j++) {
            vector<int> f(n + 1);  // 存储从第x层丢下的鸡蛋没碎的历史最值
            int x = 1;  // 从第x楼开始抛
            f[0] = 0;
            // 总楼层数
            for(int i = 1; i <= n; i++) {
                while(x < i && max(dp[x-1], f[i - x]) >= max(dp[x], f[i - x -1])) {
                    x++;
                }
                f[i] = 1 + max(dp[x-1], f[i - x]);
            }
            for(int i = 1; i <= n; i++) {
                dp[i] = f[i];
            }
        }
        return dp[n];
    }
};

复杂度分析

时间复杂度为 O ( k n ) O(kn) O(kn)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1346909.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微信小程序开发系列-10组件间通信01

微信小程序开发系列目录 《微信小程序开发系列-01创建一个最小的小程序项目》 《微信小程序开发系列-02注册小程序》 《微信小程序开发系列-03全局配置中的“window”和“tabBar”》 《微信小程序开发系列-04获取用户图像和昵称》 《微信小程序开发系列-05登录小程序》 《…

图文证明 泰勒公式展开

泰勒公式 泰勒公式简单来说就是,可以用一个N次多项式来表示出一个连续可导的函数 f(x) 是一个用函数在某点的信息描述其附近取值的公式 第一步 思考 这是一个sin(x)的图像 用函数在原点的信息描述其附近取值 用一阶导数贴合: 直接用切线来贴合就好 画一个点(0,sin(0)除的切…

内网常规攻击路径

点击星标&#xff0c;即时接收最新推文 随着网络技术的发展&#xff0c;企业内部网络架构的变化&#xff0c;网络设备多样性的增加&#xff0c;面对内网攻击&#xff0c;防御体系逐渐阶梯化&#xff0c;通过不同维度的防御联动&#xff0c;将攻击拒之门外。对于突破网络边界后进…

docker 私有仓库搭建,将镜像上传至私有仓库,从私有仓库拉取镜像

Docker 私有仓库 一、私有仓库搭建 # 1、拉取私有仓库镜像 docker pull registry # 2、启动私有仓库容器 docker run -id --nameregistry -p 5000:5000 registry # 3、打开浏览器 输入地址http://私有仓库服务器ip:5000/v2/_catalog&#xff0c;看到{"repositories&quo…

八个理由:从java8升级到Java17

目录 前言 1. 局部变量类型推断 2.switch表达式 3.文本块 4.Records 5.模式匹配instanceof 6. 密封类 7. HttpClient 8.性能和内存管理能力提高 前言 从Java 8 到 Java 20&#xff0c;Java 已经走过了漫长的道路&#xff0c;自 Java 8 以来&#xff0c;Java 生态系统…

DevExpress 皮肤改变触发后触发的事件,用来保存皮肤配置

代码&#xff1a; private UserLookAndFeel userLookAndFeel; public MainGeneral() {InitializeComponent();// 创建 UserLookAndFeel 实例userLookAndFeel new UserLookAndFeel(this);// 订阅 StyleChanged 事件userLookAndFeel.StyleChanged UserLookAndFeel_StyleChange…

学习体系结构 - AArch64内存管理

学习体系结构 - AArch64内存管理 Learn the architecture - AArch64 memory management Version 1.2 个人的英语很一般&#xff0c;对拿不准的翻译校准在后面添加了英文原文。 1、 概述 本指南介绍了AArch64中的内存转换&#xff0c;这是内存管理的关键。它解释了如何将虚拟地…

ACW741.斐波那契额数列

输入整数 N&#xff0c;求出斐波那契数列中的第 N项是多少。 斐波那契数列的第 0项是 0&#xff0c;第 1项是 1&#xff0c;从第 2 项开始的每一项都等于前两项之和。输入格式 第一行包含整数 T&#xff0c;表示共有T个测试数据。接下来 T行&#xff0c;每行包含一个整数 N。输…

Python高级用法:生成器(generator)

生成器&#xff08;generator&#xff09; 生成器是一种返回生成序列的方法&#xff0c;与直接使用列表等方式返回序列的方式不同的是&#xff0c;他的生成可以是无限的。 生成器可以与next搭配使用&#xff0c;可以被看作是一种特殊的迭代器。 yield语句 yield一般与循环相…

研究:同样的C++模板在多个cpp里出现,编译器是否要重复生成?

2023年就要过去&#xff0c;马上要跨如2024年。祝大家在新的一年&#xff0c;有个好收成。 一直以来不是很确定&#xff1a; 同样的的模板&#xff0c;在各个cpp分别出现&#xff0c;编译器要实现几份&#xff1f; 研究一下。 用命令行的编译方法&#xff0c;参考&#xff1a…

mxxWechatBot微信机器人V2使用教程(图文)最全最详细

大家伙&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂。 先看这里 mxxWechatBot功能列表一、前言二、适用人群三、准备工作四、获取账号五、下载资料 六、安装相关软件七、启动客户端八、注入并启动微信九、机器人的基本配置十、自定义接口开发 …

数据库系统概论SQL编程题合集(包含期末题、考研初试题以及复试题)

二、现有数据库casemanage中表结构如下图 1&#xff09;请编写sql语句对年龄进行升序排列 select * from afinfo order by birth;2&#xff09;请编写sql语句查询对“徐”姓开头的人员名单 select * from afinfo where name like 徐%;3&#xff09;请编写sql语句修改“陈晓”…

【28】Kotlin语法进阶——使用协程编写高效的并发程序

提示&#xff1a;此文章仅作为本人记录日常学习使用&#xff0c;若有存在错误或者不严谨得地方欢迎指正。 文章目录 一、Kotlin中的协程1.1 协程的基本用法1.1.1协程与协程作用域1.1.2 使用launch函数创建子协程1.1.3 通过suspend关键声明挂起函数1.1.4 coroutineScope函数 1.2…

对比学习简介

1. 引言 在本教程中&#xff0c;我们将介绍对比学习领域中的相关概念。首先&#xff0c;我们将讨论这种技术背后相关的理论知识&#xff1b;接着&#xff0c;我们将介绍最常见的对比学习的损失函数和常见的训练策略。 闲话少说&#xff0c;我们直接开始吧&#xff01; 2. 举…

React is not defined解决

组件未引入React报错React is not defined 神奇的是代码内并没有用到React的地方 必需要导入React才不报错 看着就很奇怪 原因是因为React创建组件需要使用到React上的一个方法createClass创建组件 在babel-loader的babel/preset-react预设里写个配置即可不用导入也不报错: //…

《深入理解C++11:C++11新特性解析与应用》笔记七

第七章 为改变思考方式而改变 7.1 指针空值--nullptr 7.1.1 指针空值&#xff1a;从0到NULL&#xff0c;再到nullptr 传统C头文件里NULL是一个宏定义&#xff1a; 在函数重载同时出现int和char *参数版本的函数时&#xff0c;使用NULL作为参数调用函数会调用int参数版本&…

机器学习部分相关概念

数据集(Data Set)即数据的集合&#xff0c;每一条单独的数据被称为样本(Sample)。 对于每个样本&#xff0c;它通常具有一些属性(Attribute)或者特征(Feature)&#xff0c; 特征所具体取得值被称为特征值(Feature Value)。 西瓜数据集 色泽根蒂纹理青绿稍蜷模糊乌黑蜷缩清晰 …

大数据 - 大数据入门第一篇 | 关于大数据你了解多少?

&#x1f436;1.1 概述 大数据&#xff08;BigData):指无法在一定时间范围内用常规软件工具进行捕捉、管理和处理的数据集合&#xff0c;是需要新处理模式才能具有更强的决策力、洞察发现力和流程优化能力的海量、高增长率和多样化的信息资产。 大数据主要解决、海量数据的采…

【Linux】内核编译 镜像制作

文章目录 一、Ubuntu内核编译1.1 为什么自己编译内核1.2 Ubuntu 内核源码下载1.21 内核的作用1.22 Linux内核与ubuntu内核1.23 Ubuntu内核源码获取 1.3 在Windows系统下编译ubuntu内核1.4 在Linux系统下编译ubuntu内核 二、镜像制作 一、Ubuntu内核编译 1.1 为什么自己编译内核…

《QDebug 2023年12月》

一、Qt Widgets 问题交流 1. 二、Qt Quick 问题交流 1.Q_REVISION 标记的信号槽或者 REVISION 标记的属性&#xff0c;在子类中访问 Q_REVISION 是 Qt 用来做版本控制的一个宏。以 QQuickWindow 为例&#xff0c;继承后去访问 REVISION 标记的 opacity 属性或者 Q_REVISION…