LeetCode 375 - 猜数字大小 II


文章目录

摘要

LeetCode 375「猜数字大小 II」是一道典型的"看起来像二分,实际上是博弈 + 动态规划"的题

很多人第一眼都会想:这不就是二分查找吗?

但一旦你真正开始算"最坏情况下要花多少钱",就会发现这题完全不是在问"猜得快不快",而是在问:

怎么猜,才能保证"无论对方选什么数字",你都不会破产。

这个问题在真实世界里非常常见,比如:

  • 风险对冲下的最坏成本评估
  • 决策树里的最坏路径控制
  • 不确定环境下的保底策略设计

这篇文章会从直觉思路一步步推导到标准解法,最后给出一份工程上稳定、好理解的 Swift 实现

描述

游戏规则可以浓缩成一句话:

  • 数字在 [1, n]
  • 你每猜错一次,就要为这次猜的数字 付钱
  • 对方会"诚实"告诉你是大了还是小了
  • 你必须保证:不管对方选哪个数字,你都有钱赢

目标不是"平均花费最少",而是:

在最倒霉的情况下,你最少需要准备多少钱,才能稳赢。

这一点非常关键,它直接决定了我们要用的是:

  • 最坏情况(max)
  • 而不是期望值或平均值

题解答案

核心思想一句话总结:

对每个区间 [l, r],枚举第一次猜的数字 k,取"左右区间最坏情况中的较大值",再加上 k 本身的成本,最终取最小的那个 k

听起来有点绕,我们拆开说。

题解代码分析

为什么这是区间 DP

假设当前要猜的范围是 [l, r]

你如果第一次猜 k

  • 猜对了:花费 0

  • 猜错了:

    • 数字在 [l, k-1]
    • [k+1, r]
    • 你要面对的是更糟的那一边

所以成本是:

text 复制代码
k + max( cost(l, k-1), cost(k+1, r) )

我们的目标是:

[l, r] 里选一个 k,让这个值 尽可能小

DP 状态定义

text 复制代码
dp[l][r] = 在区间 [l, r] 内,保证获胜所需的最小现金

边界条件:

  • l >= r:不需要猜,花费 0

状态转移:

text 复制代码
dp[l][r] = min over k in [l, r] (
    k + max(dp[l][k-1], dp[k+1][r])
)

Swift 可运行 Demo 代码

swift 复制代码
class Solution {

    func getMoneyAmount(_ n: Int) -> Int {
        if n <= 1 { return 0 }

        // dp[l][r] 表示在区间 [l, r] 内保证赢所需的最小花费
        var dp = Array(
            repeating: Array(repeating: 0, count: n + 2),
            count: n + 2
        )

        // 区间长度从 2 开始
        if n >= 2 {
            for len in 2...n {
                for l in 1...n - len + 1 {
                    let r = l + len - 1
                    dp[l][r] = Int.max

                    for k in l...r {
                        let cost = k + max(
                            k - 1 >= l ? dp[l][k - 1] : 0,
                            k + 1 <= r ? dp[k + 1][r] : 0
                        )
                        dp[l][r] = min(dp[l][r], cost)
                    }
                }
            }
        }

        return dp[1][n]
    }
}

代码为什么这样写

1. 为什么区间长度从小到大

因为:

  • dp[l][r] 依赖 dp[l][k-1]dp[k+1][r]
  • 它们的区间一定比 [l, r]

这就是典型的区间 DP 填表顺序

2. 为什么要 max

因为你不是在和一个"善良的系统"博弈,而是在和一个最坏情况博弈。

对方一定会让你走最贵的那条路。

3. 为什么不是二分

二分是为了减少猜测次数

而这道题是为了控制最坏成本

这两者在很多区间上并不一致。

示例测试及结果

swift 复制代码
let solution = Solution()

print(solution.getMoneyAmount(1))   // 0
print(solution.getMoneyAmount(2))   // 1
print(solution.getMoneyAmount(10))  // 16

输出结果:

text 复制代码
0
1
16

和题目示例完全一致。

时间复杂度

三层循环:

  • 区间长度:O(n)
  • 左端点:O(n)
  • 枚举猜测点:O(n)

总体时间复杂度:

text 复制代码
O(n³)

n <= 200 的限制下,这是完全可接受的。

空间复杂度

使用了一个 (n+2) x (n+2) 的 DP 表:

text 复制代码
O(n²)

空间换稳定性,非常值得。

总结

LeetCode 375 是一道非常标准的"最坏情况决策问题"

  • 它不是考你猜得聪不聪明
  • 而是考你是否考虑到了所有不利情况

这类题在真实工程中非常常见,比如:

  • 风控系统里的最大损失评估
  • 自动化决策中的保底策略
  • 游戏 AI 的最坏路径规划

如果你能把这道题的 DP 推清楚,其实你已经掌握了一大类博弈型区间 DP 的通用套路

相关推荐
执着25910 分钟前
力扣hot100 - 101、对称二叉树
数据结构·算法·leetcode
多恩Stone12 分钟前
【3D-AICG 系列-1】Trellis v1 和 Trellis v2 的区别和改进
人工智能·pytorch·python·算法·3d·aigc
mit6.82418 分钟前
模运算|z函数 字符串匹配
算法
阿豪只会阿巴19 分钟前
【吃饭香系列】二周目|代码随想录算法训练营第七天|454.四数相加II |383. 赎金信 |15. 三数之和 |18. 四数之和
算法
小O的算法实验室22 分钟前
2025年COR SCI2区,考虑风场影响的无人机搜救覆盖路径规划精确界算法,深度解析+性能实测
算法·无人机·论文复现·智能算法·智能算法改进
xqqxqxxq24 分钟前
洛谷算法1-3 暴力枚举(NOIP经典真题解析)java(持续更新)
java·开发语言·算法
_OP_CHEN25 分钟前
【算法基础篇】(五十五)卡特兰数封神之路:从括号匹配到二叉树构造,组合数学的万能钥匙!
算法·蓝桥杯·c/c++·组合数学·卡特兰数·算法竞赛·acm/icpc
郝学胜-神的一滴29 分钟前
Python美学的三重奏:深入浅出列表、字典与生成器推导式
开发语言·网络·数据结构·windows·python·程序人生·算法
2501_9011478329 分钟前
学习笔记:基于摩尔投票法的高性能实现与工程实践
笔记·学习·算法·性能优化
春日见32 分钟前
window wsl环境: autoware有日志,没有rviz界面/ autoware起不来
linux·人工智能·算法·机器学习·自动驾驶