P1044 [NOIP2003 普及组] 栈
[NOIP2003 普及组] 栈
题目描述
一个操作数序列,\(1,2,\ldots ,n\),栈 A 的深度大于 \(n\)。
现在可以进行两种操作,
- 将一个数,从操作数序列的头端移到栈的头端(对应数据结构栈的 push 操作)
- 将一个数,从栈的头端移到输出序列的尾端(对应数据结构栈的 pop 操作)
使用这两种操作,由一个操作数序列就可以得到一系列的输出序列。
你的程序将对给定的 \(n\),计算并输出由操作数序列 \(1,2,\ldots,n\) 经过操作可能得到的输出序列的总数。
输入格式
输入文件只含一个整数 \(n\)(\(1 \leq n \leq 18\))。
输出格式
输出文件只有一行,即可能输出序列的总数目。
样例 #1
样例输入 #1
3
样例输出 #1
5
分析
- 法1:dfs枚举所有的排列,检查每个排列是否合法,TLE
- 法2:dp思想,当前元素要么出栈,要么不出栈.
- 定义状态:f[i][j] 表示当前 i个入栈,j个出栈的情况数量
- 目标:f[n][n]
- 转移方程:
f[i][0] = 1;
f[i][j] = f[i-1][j] + f[i][j-1];
当 i==j 时,f[i][j] 只能由 f[i][j-1] 出栈一个数得到
-
法3:卡特兰数,利用卡特兰公式。
-
\(C_{i\in\{0,1,2...\}} : 1,1,2,5,14,42,132...\)
-
\(C_{i}\) 表示第 \(i\) 个卡特兰数, \(i\) 从 \(0\) 开始。
-
\(C_n=\frac {C_{2n}^{n}}{n+1}\)
-
\(C_n= \frac {C_{n-1} \ (4n-2)} {n+1}\)
-
\(C_{n+1} = \sum_{k=0}^{n} C_{k}C_{n-k} = C_{0}C_{n}+C_{1}C_{n-1}+...+C_{n}C_{0}\)
- 为何答案是卡特兰数的证明:假设 \(f_k\) 表示 \(k\) 最后出栈的方案数
- 小于 \(k\) 的元素有 \(k-1\) 个,大于 \(k\) 的元素有 \(n-k\) 个
- 所以 \(f_k=f_{k-1}f_{n-k}\)。
- 那么总方案数为 \(\sum_{k=1}^{n}f_{k}=\sum_{k=1}^{n}f_{k-1}f_{n-k}=f_0f_{n-1}+f_{1}f_{n-2}+...+f_{n-1}f_{0}=C_{n}\)。
#include<iostream>
#include<stack>
using namespace std;
typedef long long LL;
const int N= 100;
LL n, ans=0,a[N], vis[N], id[N];
// 当入栈序列为1~n, 判断出栈序列arr[l,r]是否合法
// 对于一个合法序列,不可能出现当大数出栈后,大数前的小数出栈,再大数前面的大数出栈的情况
// 如:1 4 2 3 5 6:不合法于 4 2 3 --> 3比 2后入,则 3比 2先出
bool islegal(LL*arr, int l, int r) {
for(int i=l; i<=r; i++) {
for(int j=i+1; j<=r; j++) {
if(arr[i]>arr[j]) {
for(int k=j+1; k<=r; k++) {
if(arr[j]<arr[k] && arr[i]>arr[k]) {
return 0;
}
}
}
}
}
return 1;
}
void print(LL*arr, int l, int r) {
for(int i=l; i<=r; i++) cout<<arr[i]<<" \n"[i==n];
}
void dfs(int m) {
if(m>n) {
if(islegal(a,1,n)) ans++;
return;
}
for(int i=1; i<=n; i++) {
if(!vis[i]) {
vis[i]=1, a[m]=i;
dfs(m+1);
vis[i]=0;
}
}
}
int f[N][N];
void slove2() { // dp思想
// 当前元素要么出栈,要么不出栈
// f[i][j] 表示当前 i个入栈,j个出栈的情况数量
// f[i][0] = 1;
// f[i][j] = f[i-1][j] + f[i][j-1];
// 当 i==j时,f[i][j]只能由 f[i][j-1]出栈一个数得到
for(int i=0; i<=n; i++) f[i][0]=1;
for(int i=1; i<=n; i++) {
for(int j=1; j<=i; j++) {
if(i==j) f[i][j]=f[i][j-1];
else f[i][j] = f[i-1][j]+f[i][j-1];
}
}
ans=f[n][n];
}
void slove3() { // 卡特兰数的线性递推
a[0]=1;
for(int i=1; i<=n; i++) a[i]=a[i-1]*(4*i-2)/(i+1);
ans=a[n];
}
int main() {
cin>>n;
// dfs(1);
// slove2();
slove3();
cout<<ans<<endl;
return 0;
}