入栈顺序一定,出栈的合法序列种类个数

题目

设有一个数列的输入顺序为123456,若采用栈结构,并以A和D分别表示入栈和出栈操作,试问通过进栈和出栈操作的合法序列有多少种?

解题

三种办法,结果是132种序列。

1. 列举法

不是直接一个个去列,而是找技巧。先列数列元素个数为1的,再是2的,最后一直到6个,可以找到一定的规律得出结果。

2. 代码

毕竟作为一名软件工程的学生,用老师的话说,“你们写代码的能力是要求比计算机科学的同学强的,写代码对于你们应该像喝水吃饭一样简单,这是最基础的!”。于是我在列举法之后考虑是否能用代码解决这个问题。事实证明是可以的,我的想法如下:

入栈、出栈顺序(A,D的排列)有两个要求:

1. 共有12个位置,A、D各六个;

2. 任取前n个(n<=12),A的数量应该大于等于D的数量(入了才能出啊,入的不能少于出的),且n=12时,num(A)=num(D)=6

只用遍历每一种可能即可。那么应该如何遍历?使得容易检测这两个条件,且使得时间复杂度较小(12个for循环就算了)

我的想法是利用二进制数遍历。即遍历0~2^{12}-1这4096个十进制数,转化为二进制数,为了检验第二个条件,我把12进制数中的0变为-1,1不变,只用检测前n位加起来是否>=0即可,取出每一位的方法是先除再取余。具体代码如下:

// main.cpp
// gcc编译通过
#include <iostream>
#include <stdio.h>
using namespace std;

// 10的11次方已经超过int范围
// 十进制转换为二进制,用的递归
long long transform(int num)
{
    if(!num) return 0;
    return num % 2 + 10 * transform(num / 2);
}

// 计算10的次方,目的是取数字的每一位
// C++自带的pow以及pow10返回double,不符合要求,只好自己写
long long pow10(int b)
{
    long long sum = 1;
    for (int i = 0; i < b; i++)
    {
        sum *= 10;
    }
    return sum;
}

int main()
{
    int sum = 0;
    int count = 0;
    long long num2 = 0;
    int tmp = 0;
    bool flag = false;

    // 遍历可能性
    for (int a = 4095; a >= 1; a -= 1)
    {
        sum = 0;
        flag = true;
        num2 = transform(a);
        for (int b = 11; b >= 0; b--)
        {
				// 取每一位
            tmp = (num2 / pow10(b)) % 10;
            if (tmp == 0) sum += -1;
            else if (tmp == 1) sum += 1;
            
            if (sum < 0)
            {
                flag = false;
                break;
            }
        }

        if (flag && !sum)
        {
            count++;
        }
    }
    
    cout << count <<endl;
    getchar();
    return 0;
}

3. 公式法

我认为这个题目上面两种办法还是需要懂一点脑筋的,于是问其他几个同学他们是怎么做的,结果被告知他们都是直接上网查这个题目,发现有公式,就直接拿过来解题了((⊙o⊙)…,机智啊)。

这个栈的出栈序列是卡特兰数的一个运用。

卡特兰数-度娘百科

首次出空之前第一个出栈的序数k将1~n的序列分成两个序列,其中一个是1~k-1,序列个数为k-1,另外一个是k+1~n,序列个数是n-k。

此时,我们若把k视为确定一个序数,那么根据乘法原理,f(n)的问题就等价于——序列个数为k-1的出栈序列种数乘以序列个数为n - k的出栈序列种数,即选择k这个序数的f(n)=f(k-1)×f(n-k)。而k可以选1到n,所以再根据加法原理,将k取不同值的序列种数相加,得到的总序列种数为:f(n)=f(0)f(n-1)+f(1)f(n-2)+……+f(n-1)f(0)。

卡特兰数的公式是:

\tfrac{1}{n+1}\cdot C_{2n}^{n}

其中n为元素个数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值