题目及样例描述
洛谷题目链接:P1044
对于n个不同的元素进栈,将出栈顺序记录为一组元素序列,请问总共会有多少种不同的出栈顺序的元素序列?
例如:
输入 :3
\quad
输出 :5
样例解释:
比如输入序列:123(先输入1,然后是2和3)
可能的出栈顺序为:321,132,123,213,231一共5种
思路
利用递推(递归),类似数学归纳法的思路,假设
i
i
i个元素一共有
a
[
i
]
a[i]
a[i]种出栈方式。考虑到入栈有顺序,而出栈实际上没有顺序,也就是说,每一个元素都可以最后一个出栈。我们现在让第
k
k
k个元素最后出栈。那么对于这个第
k
k
k个元素来说,有如下图示:
对于前
k
−
1
k-1
k−1个元素,共有
a
[
k
−
1
]
a[k-1]
a[k−1]种出栈方式,对于后
n
−
k
n-k
n−k个元素共有
a
[
n
−
k
]
a[n-k]
a[n−k]种出栈方式。前
k
−
1
k-1
k−1个元素出栈和后
n
−
k
n-k
n−k个元素出栈没有顺序关系,所以对于元素
k
k
k,共有
a
[
k
−
1
]
∗
a
[
n
−
k
]
a[k-1]*a[n-k]
a[k−1]∗a[n−k]种出栈方式。
现在考虑
k
k
k有多少种可能。如果我们认为数组从1开始存储。那么,
k
k
k就可以从1取到n,而
a
[
n
]
a[n]
a[n]即为我们所希望求得的数。所以有:
a
[
n
]
=
∑
k
=
1
n
a
[
k
−
1
]
∗
a
[
n
−
k
]
a[n] = \sum_{k=1}^{n}a[k-1]*a[n-k]
a[n]=k=1∑na[k−1]∗a[n−k]
显然,
a
[
1
]
=
1
a[1]=1
a[1]=1。因为长度为1的序列只有一种出栈可能。为了数学形式保持一致,我们规定
a
[
0
]
=
1
a[0]=1
a[0]=1,这样递推式就对
a
[
2
]
=
a
[
0
]
∗
a
[
1
]
+
a
[
1
]
∗
a
[
0
]
a[2]=a[0]*a[1]+a[1]*a[0]
a[2]=a[0]∗a[1]+a[1]∗a[0]也成立。
代码及代码解释
#include<bits/stdc++.h>
using namespace std;
int a[20];
int main(){
int n;
cin>>n;
a[0]=a[1]=1;
for(int i = 2;i<=n;i++){
for(int j = 1;j<=i;j++){
a[i] += a[j-1]*a[i-j];
}
}
cout<<a[n];
return 0;
}
解释:
a
[
n
]
=
∑
k
=
1
n
a
[
k
−
1
]
∗
a
[
n
−
k
]
a[n] = \sum_{k=1}^{n}a[k-1]*a[n-k]
a[n]=k=1∑na[k−1]∗a[n−k]
展开可以写成;
for(...j=1->i)
a[i] = a[i] + a[j-1]*a[i-j];
等价于:
for(...j=1->i)
a[i] += a[k-1]*a[i-k];
而上面的 i i i又可以从2取到 n n n( i = 0 , i = 1 i=0,i=1 i=0,i=1的情况已经指定为1),所以外层再加一个循环。
for(int i = 2;i<=n;i++){
//上面的代码
}
数学联系-卡特兰数
卡特兰数简介
卡特兰数是组合数学中一个常出现于各种计数问题中的数列。其前几项为(从第0项开始):1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012, 742900
…
\dots
…
这里给出一种定义式(递推公式):
可以看到,这和我们的题目要求极其相似。
对这个序列,如果我们令
0->1则有:
1->1
2->2
3->5
4->14
…
\dots
…
如果左边是输入,则右边就是程序相应的输出。
最后推荐一篇联推导卡特兰数递推式的文章
推导过程链接