[NOIP2003-B-3] 栈

描述

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

image.png


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

image.png


(原始状态如上图所示)

你的程序将对给定的 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;
}

代码解释

  1. 输入处理:读取整数 n,表示操作数序列的长度。

  2. 动态规划数组初始化:创建一个大小为 n+1 的数组 dp,其中 dp[i] 表示 i 个元素的合法出栈序列数目。初始时,dp[0] = 1,因为空序列是唯一的可能。

  3. 状态转移计算

    • 外层循环 i 从 1 到 n,依次计算每个 dp[i] 的值。
    • 内层循环 j 从 0 到 i-1,累加所有可能的组合数目 dp[j] * dp[i-1-j]dp[i]
  4. 输出结果:最终 dp[n] 即为所求的答案,直接输出。

复杂度分析

  • 时间复杂度:代码中有两层嵌套循环,时间复杂度为 O(n2)。
  • 空间复杂度:使用了长度为 n+1 的数组 dp,空间复杂度为 O(n)。

这个算法能够高效地计算出 n 最大为 18 时的结果,符合题目要求。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值