卡特兰数问题
1. 前置知识-求组合数
求组合数 C a b = C a − 1 b − 1 + C b − 1 b C^b_a = C^{b-1}_{a-1}+C^{b}_{b-1} Cab=Ca−1b−1+Cb−1b
C a b C^b_a Cab 的含义就是从 a a a 个数当中选 b b b 个数
- 如果我们不选第 a a a 个数: C a − 1 b C_{a-1}^b Ca−1b
- 如果我们选第 a a a 个数: C a − 1 b C_{a-1}^{b} Ca−1b
可以发现,就是一个动态规划的过程
- 边界条件为 C n 0 = 1 C_{n}^0=1 Cn0=1
- f [ a ] [ b ] f[a][b] f[a][b] : C a b C_a^b Cab
- 时间复杂度 O ( N 2 ) O(N^2) O(N2)
int f[N][N];
void dp()
{
f[0][0] = 1;
for(int i = 1; i < N; i ++ )
{
for(int j = 0; j < N; j ++ )
{
if(!j) f[i][j] = 1;
else f[i][j] = (f[i - 1][j] + f[i - 1][j - 1]) % mod;
}
}
}
2. 卡特兰数-例题引入
有
n
n
n(
n
n
n<=
13
13
13) 个字母
a
b
c
abc
abc…,依次进栈/出栈,有多少种可能的出栈序列
输入:一个正整数
3
3
3
输出:第一行为总方案数
例如:
3
--------
5
abc
acb
bac
bca
cba
代码一
:输出路径,时间复杂度:
O
(
2
2
n
)
O(2^{2n})
O(22n)
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <stack>
using namespace std;
vector<int> path;
stack<int> s;
int res;
void dfs(int u, int n, int cnt)
{
if(cnt == n)
{
res ++ ;
for(int i = 0; i < n; i ++ ) cout << char(path[i] + 'a');
cout << endl;
return ;
}
if(s.size()) // 可以pop
{
auto t = s.top(); s.pop();
path.push_back(t);
dfs(u, n, cnt + 1); // 由于当前元素没有使用,下一次仍然是从u开始
path.pop_back();
s.push(t);
}
if(u < n) // 可以push
{
s.push(u);
dfs(u + 1, n, cnt);
s.pop(); // 别忘了这里也需要回溯
}
}
int main()
{
int n; cin >> n;
dfs(0, n, 0);
cout << res << endl;
return 0;
}
// n=5:42
// n=3:5
代码二
:只输出答案,数据很大,注意爆
i
n
t
int
int
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 50;
long long f[N][N]; // f[i][j] -> c_i^j
int main()
{
int n; cin >> n;
for(int i = 0; i < N; i ++ ) f[i][0] = 1;
for(int i = 1; i <= n << 1; i ++ )
for(int j = 1; j <= n; j ++ )
f[i][j] = f[i - 1][j] + f[i - 1][j - 1];
cout << f[n << 1][n] / (n + 1) << endl;
return 0;
}
3. 卡特兰数
注意卡特兰数并不是只有一个形式(
1
1
+
n
C
2
n
n
\frac{1}{1+n}C_{2n}^{n}
1+n1C2nn)
ref1
ref2
ref3
4. 补充习题
不同的搜索二叉树的个数
n个节点最多可组成多少不同形态的二叉树
火车进出站问题