[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=Ca−1b+Ca−1b−1
很容易理解:在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;