描述
栈是计算机中经典的数据结构,简单的说,栈就是限制在一端进行插入删除操作的线性表。
栈有两种最重要的操作,即 pop(从栈顶弹出一个元素)和 push(将一个元素进栈)。
栈的重要性不言自明,任何一门数据结构的课程都会介绍栈。宁宁同学在复习栈的基本概念时,想到了一个书上没有讲过的问题,而他自己无法给出答案,所以需要你的帮忙。

宁宁考虑的是这样一个问题:一个操作数序列,1,2,…,n(图示为 1 到 3 的情况),栈 A 的深度大于 n。
现在可以进行两种操作,
将一个数,从操作数序列的头端移到栈的头端(对应数据结构栈的 push 操作)
将一个数,从栈的头端移到输出序列的尾端(对应数据结构栈的 pop 操作)
使用这两种操作,由一个操作数序列就可以得到一系列的输出序列,下图所示为由 1 2 3 生成序列 2 3 1 的过程。

(原始状态如上图所示)
你的程序将对给定的 n,计算并输出由操作数序列 1,2,…,n 经过操作可能得到的输出序列的总数。
输入描述
输入只含一个整数 n(1≤n≤18)。
输出描述
输出只有一行,即可能输出序列的总数目。
用例输入 1
3
用例输出 1
5
问题分析
这道题描述了一个经典的栈操作问题:给定操作数序列 1,2,…,n,通过将数字从序列头部压入栈(push),或从栈顶弹出到输出序列尾部(pop),求可能得到的不同输出序列的总数。
这个问题本质上等价于计算卡特兰数(Catalan Number)。卡特兰数在组合数学中有多种应用场景,其中之一就是描述合法的栈操作序列数目。对于给定的 n,合法的出栈序列数目为第 n 个卡特兰数。
卡特兰数的递推公式为:

其通项公式为:

解决思路
我们可以通过动态规划的方法计算卡特兰数。定义 dp[i] 表示 i 个元素可能的输出序列数目,则状态转移方程为:

边界条件为 dp[0] = 1,即当没有元素时,只有一种可能的空序列。
C++ 代码实现
下面是使用动态规划计算卡特兰数的 C++ 代码:
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n;
cin >> n;
// 初始化dp数组,dp[i]表示i个元素的合法出栈序列数目
vector<int> dp(n + 1, 0);
dp[0] = 1; // 边界条件:0个元素只有一种序列(空序列)
// 动态规划计算dp[1]到dp[n]
for (int i = 1; i <= n; ++i) {
for (int j = 0; j < i; ++j) {
dp[i] += dp[j] * dp[i - 1 - j];
}
}
cout << dp[n] << endl;
return 0;
}
代码解释
-
输入处理:读取整数
n,表示操作数序列的长度。 -
动态规划数组初始化:创建一个大小为
n+1的数组dp,其中dp[i]表示i个元素的合法出栈序列数目。初始时,dp[0] = 1,因为空序列是唯一的可能。 -
状态转移计算:
- 外层循环
i从 1 到n,依次计算每个dp[i]的值。 - 内层循环
j从 0 到i-1,累加所有可能的组合数目dp[j] * dp[i-1-j]到dp[i]。
- 外层循环
-
输出结果:最终
dp[n]即为所求的答案,直接输出。
复杂度分析
- 时间复杂度:代码中有两层嵌套循环,时间复杂度为 O(n2)。
- 空间复杂度:使用了长度为
n+1的数组dp,空间复杂度为 O(n)。
这个算法能够高效地计算出 n 最大为 18 时的结果,符合题目要求。
951

被折叠的 条评论
为什么被折叠?



