卡特兰数 与 组合数计算(# [NOIP2003 普及组] 栈)

[NOIP2003 普及组] 栈

初见这个题目时还没有接触过卡特兰数,准备用DFS遍历,但是怎么都想不出如何下手,于是我点开了算法标签…

卡特兰数思想

对于进栈我们可以看成+1,出栈我们可以看成-1
那么对于1 3 2 这种组合序列就可以表示为 +1 -1 +1 +1 -1 -1(分别代表1进栈,1出栈,2进栈 ,3进栈,3出栈,2出栈。
由此我们可以看出,对于合法序列,其前缀和必然满足恒大于等于0(因为每一个-1前面一定有一个+1与之对应,否则就是非法序列),因此,只要前缀和存在小于0,那么就是非法序列。
为什么需要强调非法序列与合法序列呢?(下文的序列B并不合法,因为+1与-1个数不等)(只是为了确定A的数量而想出的方法)
举个例子:对于 +1 -1 -1 -1 +1 +1这个序列,很明显它是非法的,我们将“第一个前缀和小于0的前缀”取反,得到-1 +1 +1 -1 +1 +1,则有n+1个+1和n-1个-1(因为“第一个前缀和小于0的前缀必然=-1)
假设每个非法序列A,对应的序列为B,每个A只有一个”第一个前缀和小于0的前缀“,因此每个A只会对应一个B。
因为每个B都有n+1个+1,n-1个-1,所以B的数量为 C 2 n n + 1 C^{n+1}_{2n} C2nn+1,又因为每个A对应一个B,所以A的数量也为 C 2 n n + 1 C^{n+1}_{2n} C2nn+1
序列总数(即排列组合数) = C 2 n n C^{n}_{2n} C2nn(2n个位置中选n个+1)
所以合法序列数 = C 2 n n C^{n}_{2n} C2nn- C 2 n n + 1 C^{n+1}_{2n} C2nn+1= C 2 n n n + 1 \frac{C^{n}_{2n}}{n+1} n+1C2nn
不仅在栈这题可以利用这种思想,括号序列、二叉树等都可以向卡特兰数思想考虑。


既然公式已经出现,那么离AC就不远了,看见排列组合,第一想到的就是阶乘了,发现用阶乘函数(自己写的)很快就过了样例,交上去却只有60分,即使开了long long 也不行。那么就要考虑组合数的计算方法了。

组合数的计算方法

组合数计算的递推公式: C a b = C a − 1 b + C a − 1 b − 1 C^{b}_{a} = C^{b}_{a-1}+C^{b-1}_{a-1} Cab=Ca1b+Ca1b1
很容易理解:在a个苹果中拿出一个苹果,只有两种可能:
1、不拿这个苹果然后取b个苹果。
2、拿这个苹果再在a-1个苹果中拿b-1个苹果。
OK,到这里就能顺利AC这个题目了。
AC代码如下:(其中f[b][a]代表 C a b C^{b}_{a} Cab

#include <bits/stdc++.h>
using namespace std;

long long f[40][40];
long long add(int b,int a)
{
    for(int i = 0;i<=a;i++)
    for(int j = 0;j<=i;j++)
    {
        if(!j)f[j][i] = 1;
        else f[j][i] = f[j][i-1]+f[j-1][i-1];
    }
    return f[b][a];
}


int main()
{
    int n;
    cin >> n;
    cout << add(n,2*n)/(n+1);
}

只需要将 C n 0 C^{0}_{n} Cn0这种情况赋值为1即可,因为 C n n + 1 C^{n+1}_{n} Cnn+1这种情况不会出现,f[n+1][n]一直都为0;

  • 9
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值