这道题其实等价于下面这个题目:
有 1 1 ~这 N N 个自然数和一个无限大的栈,没个书都要进栈并出栈一次,如果进栈的顺序为,那么可能的出栈序列有多少种
方法一 搜索(枚举/递推), O(2N) O ( 2 N )
一个很直观的想法是,面对任何一种状态我们只有两种选择
1.把下一个数进栈
2.把当前栈顶的数出栈(如果栈非空)
用递归实现这个程序,时间复杂度为 O(2N) O ( 2 N ) ,这样就得到了所有可能的出栈序列方案。
方法二 递推 O(N2) O ( N 2 )
设 SN S N 表示进栈顺序为 1,2,...,N 1 , 2 , . . . , N 时可能的出栈序列综述
划分子问题后可以划分成 k−1 k − 1 个数进出栈和 N−k N − k 个数进出栈这两个子问题,得到递推公式
SN=∑k=1NSk−1×SN−k
S
N
=
∑
k
=
1
N
S
k
−
1
×
S
N
−
k
方法三 动态规划 O(N2) O ( N 2 )
设 F[i][j] F [ i ] [ j ] 表示有 i i 个数尚未进栈,目前有个数在栈里,剩下的数都已出栈时的方案总数
在最终状态下,所有数都已出栈,得到边界 F[0][0]=1 F [ 0 ] [ 0 ] = 1
最终目标是所有数尚未进栈时可以得到上述边界的总数,所以目标为 F[N][0] F [ N ] [ 0 ]
每一步的两种决策分别是入栈和出栈,所以得到方程:
F[i][j]=F[i−1][j+1]+f[i][j−1]
F
[
i
]
[
j
]
=
F
[
i
−
1
]
[
j
+
1
]
+
f
[
i
]
[
j
−
1
]
方法四 数学 O(N) O ( N ) ~ O( O ( 能过 ) )
该问题等价与求第项 Catalan C a t a l a n 数,即 CN2N/(N+1) C 2 N N / ( N + 1 ) (当然, Catalan C a t a l a n 数的求法不止这一个,其他题解中都有写到,这里就不详细提及了)
对于 C(x,y) C ( x , y ) 的求法,有以下主要的两种
1. C(x,y)=x!/y!/(x−y)! C ( x , y ) = x ! / y ! / ( x − y ) !
2. C(x,y)=C(x−1,y−1)+C(x−1,y) C ( x , y ) = C ( x − 1 , y − 1 ) + C ( x − 1 , y )
注意,第一种求法可能会爆出范围最好是直接用第二种。
代码 阶乘求Catalan 60分
#include<cstdio>
using namespace std;int n;
unsigned long long jc[101];
long long c(int x,int y)
{
return jc[x]/jc[y]/jc[x-y];//通式
}
int main()
{
scanf("%d",&n);
jc[0]=1;//0的阶乘就是1
for(int i=1;i<101;i++) jc[i]=jc[i-1]*i;//计算
printf("%lld",c(n<<1,n)/(n+1));//输出
}
Ac代码
#include<cstdio>
using namespace std;int n;
long long C[41][41];
long long c(int x,int y)//杨辉金字塔求法
{
if(x==y||!y) return 1;
if(y==1) return x;
if(C[x][y]) return C[x][y];
return C[x][y]=c(x-1,y)+c(x-1,y-1);
}
int main()
{
scanf("%d",&n);
printf("%lld",c(n<<1,n)/(n+1));
}