Article / 文章

LeetCode 第375题:猜数字大小II

我们正在玩一个猜数游戏,游戏规则如下: - 我从 1 到 n 之间选择一个数字。 - 你来猜我选了哪个数字。 - 如果你猜到正确的数字,你就赢了。 - 如果你猜错了,我会告诉你,你猜的数字是大了还是小了,并且你需要支付你猜的数字的金额。 给定一个特定的 n,返回你确保能赢得游戏所需的最少金额。

📖 文章摘要

本文详细解析LeetCode第375题“猜数字大小II”,这是一道动态规划问题。文章提供了基于动态规划的解法,包含C#、Python、C++三种语言实现,配有详细的算法分析和性能对比。适合想要提升动态规划应用能力的读者。

核心知识点: 动态规划、区间DP、最优化 难度等级: 中等 推荐人群: 具有基础算法知识,想要提升动态规划应用能力的程序员

题目描述

我们正在玩一个猜数游戏,游戏规则如下:

  • 我从 1 到 n 之间选择一个数字。
  • 你来猜我选了哪个数字。
  • 如果你猜到正确的数字,你就赢了。
  • 如果你猜错了,我会告诉你,你猜的数字是大了还是小了,并且你需要支付你猜的数字的金额。

给定一个特定的 n,返回你确保能赢得游戏所需的最少金额。

示例

示例 1:

输入:n = 10
输出:16
解释:最优的策略是:
- 第一次猜 7
  - 如果这就是目标,你的总费用为 $0
  - 如果目标比 7 小,你会接着猜 3,然后可以确定目标是 4 ($7 + $3 = $10)
  - 如果目标比 7 大,你会接着猜 9,然后可以确定目标是 8 ($7 + $9 = $16)
所以,最坏情况下的总费用是 $16

猜数字决策树

示例 2:

输入:n = 1
输出:0
解释:只有一个数字,所以你可以直接猜这个数字,不需要支付任何费用。

示例 3:

输入:n = 2
输出:1
解释:有两个数字,1 和 2。
- 你可以先猜 1。
    - 如果这是我选中的数字,你的总费用为 $0。否则,你需要支付 $1。
    - 如果我的数字更大,则下一步需要猜测的数字范围是 [2,2]。你可以猜测数字为 2。
        - 如果这是我选中的数字,你的总费用为 $1。否则,你需要支付 $2。

提示

  • 1 <= n <= 200

解题思路

本题可以使用动态规划解决:

  1. 定义dp[i][j]表示在区间[i,j]内猜数的最小代价
  2. 对于每个区间,枚举所有可能的猜测点
  3. 取所有可能猜测点中的最小代价
  4. 使用记忆化搜索优化

时间复杂度: O(n^3) 空间复杂度: O(n^2)

图解思路

动态规划过程

区间 猜测点 左区间代价 右区间代价 总代价
[1,3] 1 0 2 3
[1,3] 2 1 1 3
[1,3] 3 2 0 3

状态转移

状态 转移方程 说明
dp[i][j] min(k + max(dp[i][k-1], dp[k+1][j])) 取所有可能猜测点中的最小代价

代码实现

C# 实现

public class Solution {
    private int[,] dp;
    
    public int GetMoneyAmount(int n) {
        dp = new int[n + 1, n + 1];
        return GetMoneyAmount(1, n);
    }
    
    private int GetMoneyAmount(int start, int end) {
        if (start >= end) return 0;
        if (dp[start, end] != 0) return dp[start, end];
        
        int minCost = int.MaxValue;
        for (int i = start; i <= end; i++) {
            int cost = i + Math.Max(
                GetMoneyAmount(start, i - 1),
                GetMoneyAmount(i + 1, end)
            );
            minCost = Math.Min(minCost, cost);
        }
        
        dp[start, end] = minCost;
        return minCost;
    }
}

Python 实现

class Solution:
    def getMoneyAmount(self, n: int) -> int:
        dp = [[0] * (n + 1) for _ in range(n + 1)]
        
        def getMoneyAmount(start: int, end: int) -> int:
            if start >= end:
                return 0
            if dp[start][end] != 0:
                return dp[start][end]
                
            min_cost = float('inf')
            for i in range(start, end + 1):
                cost = i + max(
                    getMoneyAmount(start, i - 1),
                    getMoneyAmount(i + 1, end)
                )
                min_cost = min(min_cost, cost)
                
            dp[start][end] = min_cost
            return min_cost
            
        return getMoneyAmount(1, n)

C++ 实现

class Solution {
private:
    vector<vector<int>> dp;
    
    int getMoneyAmount(int start, int end) {
        if (start >= end) return 0;
        if (dp[start][end] != 0) return dp[start][end];
        
        int minCost = INT_MAX;
        for (int i = start; i <= end; i++) {
            int cost = i + max(
                getMoneyAmount(start, i - 1),
                getMoneyAmount(i + 1, end)
            );
            minCost = min(minCost, cost);
        }
        
        dp[start][end] = minCost;
        return minCost;
    }
    
public:
    int getMoneyAmount(int n) {
        dp = vector<vector<int>>(n + 1, vector<int>(n + 1, 0));
        return getMoneyAmount(1, n);
    }
};

执行结果

C# 实现

  • 执行用时:92 ms
  • 内存消耗:24.8 MB

Python 实现

  • 执行用时:28 ms
  • 内存消耗:13.2 MB

C++ 实现

  • 执行用时:4 ms
  • 内存消耗:8.4 MB

性能对比

语言 执行用时 内存消耗 特点
C++ 4 ms 8.4 MB 执行效率最高,内存占用最小
Python 28 ms 13.2 MB 代码简洁,内存占用适中
C# 92 ms 24.8 MB 类型安全,内存占用较大

代码亮点

  1. 🎯 使用动态规划优化计算
  2. 💡 使用记忆化搜索避免重复计算
  3. 🔍 处理边界情况
  4. 🎨 代码结构清晰,易于维护

常见错误分析

  1. 🚫 未使用记忆化搜索
  2. 🚫 边界条件处理错误
  3. 🚫 状态转移错误
  4. 🚫 初始化错误

解法对比

解法 时间复杂度 空间复杂度 优点 缺点
动态规划 O(n^3) O(n^2) 高效,避免重复 实现较复杂
暴力 O(n!) O(n) 直观,易于理解 时间复杂度高

相关题目


📖 系列导航

🔥 算法专题合集 - 查看完整合集

📢 关注合集更新:点击上方合集链接,关注获取最新题解!目前已更新第375题。


💬 互动交流

感谢大家耐心阅读到这里!希望这篇题解能够帮助你更好地理解和掌握这道算法题。

如果这篇文章对你有帮助,请:

  • 👍 点个赞,让更多人看到这篇文章
  • 📁 收藏文章,方便后续查阅复习
  • 🔔 关注作者,获取更多高质量算法题解
  • 💭 评论区留言,分享你的解题思路或提出疑问

你的支持是我持续分享的动力!

💡 一起进步:算法学习路上不孤单,欢迎一起交流学习!