火龙信奥
  • 首页
  • 课程
  • 题库
  • 打卡
    • 代码对战
    • 快速对战
  • 题单
  • 团队
  • 荣誉墙
  • 商城
  • 登录 / 注册

CSP2020初赛第17题 - 鸡蛋掉落问题

作者: 作者的头像   huolong , 时间:2026-08-07 13:01:13 , 所有人可见, 阅读  29

这是一份关于经典算法问题“鸡蛋掉落问题(Egg Dropping Puzzle)”的代码分析。


1. 题目描述 (Markdown 格式)

题目名称:鸡蛋掉落问题

问题描述: 现有 $n$ 层楼和 $m$ 个鸡蛋。你需要确定一个临界楼层 $F$($0 \le F \le n$),使得在 $F$ 层或以下掉落鸡蛋不会碎,而在 $F$ 层以上掉落则会碎。 当你丢下一个鸡蛋: 1. 如果鸡蛋碎了,你就不能再使用它,且你需要检查该层以下的楼层。 2. 如果鸡蛋没碎,你可以继续使用它,且你需要检查该层以上的楼层。

目标: 求在最坏情况下,最少需要实验多少次才能确定临界楼层 $F$。

输入: 两个正整数 $n$(楼层数)和 $m$(鸡蛋数)。

输出: 第一行:使用递归函数 f(n, m) 计算的结果。 第二行:使用动态规划函数 g(n, m) 计算的结果。


2. 代码注释与逻辑分析

#include <algorithm>
#include <iostream>
#include <limits>

using namespace std;

const int MAXN = 105;
const int MAXK = 105;

int h[MAXN][MAXK]; // 存储动态规划状态的数组,h[i][j] 表示 i 层楼 j 个鸡蛋的最少实验次数

// 递归方案:未优化(无记忆化),存在大量重复计算
int f(int n, int m)
{
    if (m == 1) return n; // 只有一个鸡蛋,必须从 1 层逐层向上尝试,最坏需要 n 次
    if (n == 0) return 0; // 0 层楼不需要实验

    int ret = numeric_limits<int>::max();
    // 遍历在第 i 层丢下鸡蛋的所有可能性
    for (int i = 1; i <= n; i++)
        // max(...) 代表最坏情况:
        // f(i-1, m-1): 鸡蛋碎了,剩下 m-1 个鸡蛋,需检查下面 i-1 层
        // f(n-i, m): 鸡蛋没碎,剩下 m 个鸡蛋,需检查上面 n-i 层
        // +1 代表本次实验
        ret = min(ret, max(f(n - i, m), f(i - 1, m - 1)) + 1);
    return ret;
}

// 动态规划方案:自底向上
int g(int n, int m)
{
    // 初始化基本状态
    for (int i = 1; i <= n; i++)
        h[i][1] = i; // 只有 1 个鸡蛋时,i 层楼需要 i 次
    for (int j = 1; j <= m; j++)
        h[0][j] = 0; // 0 层楼需要 0 次

    // 状态转移过程
    for (int i = 1; i <= n; i++){          // 遍历楼层
        for (int j = 2; j <= m; j++){      // 遍历鸡蛋数
            h[i][j] = numeric_limits<int>::max();
            for (int k = 1; k <= i; k++)   // 遍历第一次抛掷的楼层 k
                h[i][j] = min(
                    h[i][j],
                    max(h[i - k][j], h[k - 1][j - 1]) + 1
                );
        }
    }
    return h[n][m];
}

int main()
{
    int n, m;
    cin >> n >> m;
    // 输出两遍结果,逻辑上 f 和 g 是等价的
    cout << f(n, m) << endl << g(n, m) << endl;
    return 0;
}

3. 状态转移与复杂度分析

状态转移方程

设 $dp[i][j]$ 为 $i$ 层楼、$j$ 个鸡蛋时的最优解: $$dp[i][j] = \min_{1 \le k \le i} { \max(dp[i-k][j], dp[k-1][j-1]) + 1 }$$

复杂度分析

  1. 递归函数 f(n, m):
  2. 由于没有使用记忆化搜索,其复杂度呈指数级增长。
  3. 对于 $n=100, m=100$,递归深度和分支数会导致运行时间极长(无法在正常时间内跑完)。
  4. 动态规划 g(n, m):
  5. 时间复杂度:$O(n^2 \cdot m)$。共有 $n \times m$ 个状态,每个状态需要遍历 $O(n)$ 次来寻找最优抛掷点。
  6. 空间复杂度:$O(n \cdot m)$。使用了一个二维数组存储状态。

4. 题目解析

判断题

  1. 当输入为 7 3 时,第 19 行用来取最小值的 min 函数执行了 449 次。

    • 答案:B (错误)
    • 解析:设 $C(n, m)$ 为 min 执行次数。 根据递归式 $C(n, m) = n + \sum_{i=1}^n (C(n-i, m) + C(i-1, m-1))$。 已知 $C(n, 1) = 0, C(0, m) = 0$。 计算得:$C(n, 2) = 2^n - 1$。 $C(7, 3) = 2 \cdot C(6, 3) + 2^6 = 2(192) + 64 = 448$。 实际执行了 448 次而非 449 次。
  2. 输出的两行整数总是相同的。

    • 答案:A (正确)
    • 解析:f 是递归实现,g 是迭代实现,两者逻辑完全一致,均解同一个方程。
  3. 当 m 为 1 时,输出的第一行总为 n。

    • 答案:A (正确)
    • 解析:代码第 14 行 if (m == 1) return n; 直接处理了此基准情况。

单选题

  1. 算法 g(n, m) 最为准确的时间复杂度分析结果为( )。

    • 答案:C ($O(n^2 m)$)
    • 解析:如前分析,外层两层循环 $n \times m$,内层查找 $k$ 经历 $n$ 次,总复杂度 $O(n \cdot m \cdot n) = O(n^2 m)$。
  2. 当输入为 20 2 时,输出的第一行为( )。

    • 答案:C (6)
    • 解析:对于 2 个鸡蛋,$x$ 次实验最高能测出的楼层公式为 $\frac{x(x+1)}{2}$。 若 $x=5$,$\frac{5 \times 6}{2} = 15 < 20$; 若 $x=6$,$\frac{6 \times 7}{2} = 21 \ge 20$。因此最少需要 6 次。
  3. 当输入 100 100 时,输出的第一行为( )。

    • 答案:B (7)
    • 解析:当鸡蛋数 $m$ 非常充足时($m \ge \log_2 n$),最优策略是二分查找。 $\log_2 100 \approx 6.64$。 $2^6 = 64 < 100$,$2^7 = 128 > 100$。 因此对于 100 层楼,只要鸡蛋够多,最坏情况下 7 次必出结果。

—— 本文来自火龙信奥(义乌睿码科技):义乌青少年信息学奥赛与编程教育平台,专注 CSP-J/S、NOIP、GESP 竞赛培训,线上线下融合教学,助力编程升学。网址:hlcoding.com

关于火龙

  • 关于我们
  • 预约试听
  • ACM课程
  • CSP课程
  • 学习指南

帮助中心

  • 用户协议
  • 打字练习
  • 在线画图
  • DevC++下载
  • CSP报名
  • GESP官网

推荐课程

  • C++零基础入门(可试看)
  • C++进阶提升
  • GESP考级辅导
  • GESP打卡
  • CSP-J/S打卡

公众号

火龙信奥公众号二维码

抖音号

火龙信奥抖音号二维码

地址:义乌市北门街188号新天地商厦二楼2F 邮箱:wdlok305@126.com

© 2017-2026 义乌市睿码科技有限公司版权所有 浙ICP备2021013995号

火龙信奥
请输入登录信息


请完成安全验证
验证码底图 滑块
向右拖动滑块完成验证
请输入用户名 / 绑定的手机号码



请输入注册信息(手机号验证码注册)





验证码5分钟有效,60秒内不可重复获取,每日最多3次

微信登录

微信登录二维码

正在生成二维码...

账号已过期,请续期。
去续期

绑定手机号

📱

为了更好地保护您的账号安全,享受完整的平台服务

请您尽快绑定手机号码