今天一道中等题 套碗游戏的取碗顺序问题(2)
问题描述
小F正在玩一个套碗的游戏,每个碗都有一个编号,从1到n,它们从大到小被套在一根木棍上。小F只能从木棍上取最上面的碗,每次只能取一个。现在你需要计算总共有多少种取碗的顺序。
例如,对于2个碗,取碗的顺序可以是 2 1 或 1 2,这两种顺序都是合法的。而对于3个碗,给定顺序 3 1 2 不可能通过合法操作实现,因此该顺序不可行。
测试样例
样例1:
输入:M = 2
输出:2
样例2:
输入:M = 3
输出:5
样例3:
输入:M = 4
输出:14
日常分析
这是一个关于栈的组合问题,我们可以将问题抽象为栈的出栈顺序问题。也就是问题可以被看作是从一堆碗(编号从 1 到 M)中取碗的合法顺序。我们只能从栈的顶部取出碗,这意味着合法的取碗顺序满足以下条件:对于任何一个碗,它只能在比它大的碗已经被取出之后才被取出。因此,我们要找出所有可能的合法出栈顺序。
问题转化
给定 M 个碗,取出顺序的合法性是一个典型的栈操作问题。可以通过如下方式理解:
- 栈的操作:每次从栈中取出碗相当于执行一次
pop操作,但栈的操作必须遵循先进后出原则。 - 合法顺序的条件:如果一个编号较小的碗在栈中已经被压入,但它还没有被取出,那么较大的碗必须先被取出(也就是先执行
pop),然后才能取出较小的碗。
这类问题可以通过卡塔兰数来解决。卡塔兰数是一个经典的递归组合问题,能够表示给定数量的元素,生成合法的栈操作序列数。不清楚的伙计可以点击链接详细了解一下。
卡塔兰数公式
卡塔兰数 C(n) 可以通过以下递归公式计算:
即:C(n) 是由两个子问题组成的:第一个子问题是将 i 个碗取出,第二个子问题是将剩下的 n-i-1 个碗取出。
思考步骤
- 初始情况:当只有一个碗时,只有一种合法的取出顺序,即
C(1) = 1。 - 递归推导:假设我们已经计算了
C(n-1)的值,那么通过递归公式,我们可以计算出C(n)。 - 结果输出:当 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]