青训营X豆包MarsCode技术训练营|豆包MarsCode AI刷题

61 阅读3分钟

今天一道中等题 套碗游戏的取碗顺序问题(2)

问题描述

小F正在玩一个套碗的游戏,每个碗都有一个编号,从1到n,它们从大到小被套在一根木棍上。小F只能从木棍上取最上面的碗,每次只能取一个。现在你需要计算总共有多少种取碗的顺序。

例如,对于2个碗,取碗的顺序可以是 2 11 2,这两种顺序都是合法的。而对于3个碗,给定顺序 3 1 2 不可能通过合法操作实现,因此该顺序不可行。


测试样例

样例1:

输入:M = 2
输出:2

样例2:

输入:M = 3
输出:5

样例3:

输入:M = 4
输出:14

日常分析

这是一个关于栈的组合问题,我们可以将问题抽象为栈的出栈顺序问题。也就是问题可以被看作是从一堆碗(编号从 1 到 M)中取碗的合法顺序。我们只能从栈的顶部取出碗,这意味着合法的取碗顺序满足以下条件:对于任何一个碗,它只能在比它大的碗已经被取出之后才被取出。因此,我们要找出所有可能的合法出栈顺序。

问题转化

给定 M 个碗,取出顺序的合法性是一个典型的栈操作问题。可以通过如下方式理解:

  1. 栈的操作:每次从栈中取出碗相当于执行一次 pop 操作,但栈的操作必须遵循先进后出原则。
  2. 合法顺序的条件:如果一个编号较小的碗在栈中已经被压入,但它还没有被取出,那么较大的碗必须先被取出(也就是先执行 pop),然后才能取出较小的碗。

这类问题可以通过卡塔兰数来解决。卡塔兰数是一个经典的递归组合问题,能够表示给定数量的元素,生成合法的栈操作序列数。不清楚的伙计可以点击链接详细了解一下。

卡塔兰数公式

卡塔兰数 C(n) 可以通过以下递归公式计算:

C(0)=1C(0) = 1
C(n)=i=0n1C(i)×C(ni1)for n1C(n) = \sum_{i=0}^{n-1} C(i) \times C(n-i-1) \quad \text{for } n \geq 1

即:C(n) 是由两个子问题组成的:第一个子问题是将 i 个碗取出,第二个子问题是将剩下的 n-i-1 个碗取出。

思考步骤

  1. 初始情况:当只有一个碗时,只有一种合法的取出顺序,即 C(1) = 1
  2. 递归推导:假设我们已经计算了 C(n-1) 的值,那么通过递归公式,我们可以计算出 C(n)
  3. 结果输出:当 M 为输入时,计算并输出 C(M)

案例分析

样例 1: M = 2

对于 M = 2,所有合法的取碗顺序是:

  • 2 1
  • 1 2

共 2 种合法的顺序,C(2) = 2

代码实现

我们使用动态规划方法计算卡塔兰数。通过存储中间结果,避免重复计算,提高效率。

def solution(M):
    # 用一个数组 dp 来存储 C(n) 的值
    dp = [0] * (M + 1)
    dp[0] = 1  # C(0) = 1

    # 根据递归公式计算 C(1) 到 C(M)
    for n in range(1, M + 1):
        dp[n] = 0
        for i in range(n):
            dp[n] += dp[i] * dp[n - 1 - i]

    return dp[M]