卡特兰数
- 注CSDN这里图片好像不支持缩放这样子,而且这公式支持真的稀烂。。
参考
卡特兰数定义
-
卡特兰数是一种经典的组合数,经常出现在各种计算中,其前几项为 : 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012, 742900, 2674440, 9694845, 35357670, 129644790, 477638700, 1767263190, 6564120420, 24466267020, 91482563640, 343059613650, 1289904147324, 4861946401452, ...
-
卡特兰数到递推式:
- f ( n ) = ∑ i = 0 n − 1 f ( i ) ∗ f ( n − 1 − i ) f(n) = \sum_{i=0}^{n-1}f(i)\ast f(n-1-i) f(n)=i=0∑n−1f(i)∗f(n−1−i)
-
卡特兰数到通式:
- f ( n ) = C 2 n n n + 1 f(n) = \frac{C_{2n}^n}{n+1} f(n)=n+1C2nn
从递推式到通式到证明——母函数法
-
上面提到,卡特兰数到递推式可以写成:
-
那么卡特兰数到母函数(bilibili-母函数)可以设成:
- 由于递推式中出现了两个元素乘积,因此,先求 F 2 ( x ) F^2(x) F2(x)
- 于是我们可以取错位相减:
- 然后解方程(看成 a x 2 − x = − 1 ax^2-x = -1 ax2−x=−1)得:
- 由 lim x − > 0 + F ( x ) = f ( 0 ) = 1 \lim_{x->0^+} F(x) = f(0) = 1 limx−>0+F(x)=f(0)=1 可得:
-
很显然,应该保留 F ( x ) = 1 − 1 − 4 x 2 x F(x) = \frac{1 - \sqrt {1 - 4x}}{2x} F(x)=2x1−1−4x
-
接下将F(x)用级数拆开:
-
又因为 F ( x ) = ∑ n = 0 ∞ f ( n ) x n F(x) = \sum_{n=0}^\infty f(n)x^n F(x)=∑n=0∞f(n)xn,我们可以得出 f ( n ) f(n) f(n)的公式为:
-
f ( n ) = ( 2 n n ) n + 1 = = C 2 n n n + 1 f(n) = \frac{ \left( \begin{array}{c} 2n \\ n \end{array} \right)}{n+1} = =\frac{C_{2n}^n}{n+1} f(n)=n+1(2nn)==n+1C2nn
-
至此得到通项,于是我们知道了通项是怎么来的了
-
从通式到通式的递推公式推导
- 这部分原本是写在作业里的,因此命名可能会有点不一致,总之可以用通式的下一项
C
a
t
a
l
a
n
n
−
1
=
C
2
n
−
2
n
−
1
n
=
1
n
∗
(
2
n
−
2
)
!
(
n
−
1
)
!
∗
(
n
−
1
)
!
Catalan_{n-1} = \frac{C^{n-1}_{2n-2}}{n} = \frac{1}{n}*\frac{(2n-2)!}{(n-1)!*(n-1)!}
Catalann−1=nC2n−2n−1=n1∗(n−1)!∗(n−1)!(2n−2)!得到递推式:
通用模板
-
模板式根据通式的递推公式来写的
-
def catalan(self, n: int) -> int: if n == 0 or n == 1: return 1 return (4 * n - 2) * self.catalan(n - 1) // (n + 1)
应用举例
进出栈序列
-
题目描述
n 个元素进栈序列为:
1,2,3,4,...,n
,则有多少种出栈序列 -
-
找张图来说明:
- 我们假设第k元素( k ∈ 1 ⋯ n k\in 1\cdots n k∈1⋯n)为最后一个出栈的元素,那么在此之前有k-1个元素已经出栈了,在此之后还有n-k-1个元素需要在k之前出栈,那么我们可以得到两部分: f ( k − 1 ) ∗ f ( n − k − 1 ) f(k-1)\ast f(n-k-1) f(k−1)∗f(n−k−1),其中 f ( n ) f(n) f(n)表示计算有n个元素的出栈可能数的函数
- 然后( k ∈ 1 ⋯ n k\in 1\cdots n k∈1⋯n),我们就得到了 f ( n ) = ∑ i = 1 n f ( i ) ∗ f ( n − i ) f(n) = \sum_{i=1}^{n}f(i)\ast f(n-i) f(n)=∑i=1nf(i)∗f(n−i),这就是卡特兰数的递推式~,于是可以直接用通式来解了
括号序列
-
题目描述
n 对括号,则有多少种 “括号匹配” 的括号序列
-
找张图来说明:
- 这个问题其实和进出栈道问题十分类似,一般用栈解决(即使不用卡特兰),我们假设第k个‘(’是最后一个出栈道括号,那么之前有k-1个‘(’已经出栈,之后还有n-k-1个‘(’括号。。。。。。然后套公式
二叉树
-
题目描述
- n + 1 个叶子节点能够构成多少种形状不同的(国际)满二叉树
- (国际)满二叉树定义:如果一棵二叉树的结点要么是叶子结点,要么它有两个子结点,这样的树就是满二叉树
-
根据国际满二叉树的定义,由n + 1 个叶子节点可知二叉树一共有2n个节点
-
我们不妨设二叉树扩展的时候为先左叶子节点后右叶子节点的顺序(因为这个不影响结果,反过来也是可以的,但是一定要左右都有才满足条件)
-
然后不妨假设第2k个节点是最后被扩展的右节点,在此之前(即该节点的兄弟节点被扩展之前)有k-1个右节点被扩展,之后(即该节点的兄弟节点被扩展之后)有n-k-1个右节点要在这个之前被扩展。。。然后就变成了括号匹配问题。。。然后用模版套上去就好了
电影购票
- 题目描述
- 电影票一张 50 coin,且售票厅没有 coin。m 个人各自持有 50 coin,n 个人各自持有 100 coin。则有多少种排队方式,可以让每个人都买到电影票。
- 持有 50 coin 的人每次购票时不需要找零,并且可以帮助后面持有 100 coin 的人找零;而对于持有 100 coin 的人每次购票时需要找零,但 100 coin 对后面的找零没有任何作用。因此,**相当于每个持有 100 coin 的人都需要和一个持有 50 coin 的人进行匹配。**于是就要求50在买的时候一定有100在此等候找零,此时又回到了括号匹配问题(进出栈问题)。。。然后套模板
凸n边形的不同划分方式
-
这道是作业题:
-
Given a convex polygon with n vertices, we can divide it into several separated pieces, such that every piece is a triangle. When n = 4, there are two different ways to divide the polygon; When n = 5, there are five different ways. Give an algorithm that decides how many ways we can divide a convex polygon with n vertices into triangles.
-
大概意思就是凸n边形的三角形划分种类,如凸4边形右2种,凸5边形有5种这样子
-
思路大概是这样的,以五边形为例,我们先编号,然后固定一个点(这里是3):
-
-
然后把多边形划分成两个部分,左侧是k+1边形(4边形),下册是m-k+1边形(5-3+1),于是可以写出 f ( 4 ) ∗ f ( 5 + 1 − 3 ) f(4)\ast f(5+1-3) f(4)∗f(5+1−3)
- (为了后面好区分,我把n换成了m)
-
归纳一下就可以写出递推式 f ( n ) = ∑ i = 2 m − 2 f ( i + 1 ) ∗ f ( m + 1 − i ) f(n) = \sum_{i=2}^{m-2}f(i+1)\ast f(m+1-i) f(n)=∑i=2m−2f(i+1)∗f(m+1−i),但是我们发现这好像一点区别,为了保持一致,令m-2=n,于是得到 f ( n ) = ∑ i = 0 n − 1 f ( i ) ∗ f ( n − 1 − i ) f(n) = \sum_{i=0}^{n-1}f(i)\ast f(n-1-i) f(n)=∑i=0n−1f(i)∗f(n−1−i),这就可以套公式了,区别就是5边形这个玩意相当于卡特兰的n=3这样子
-
-
代码:
-
class Solution: def catalan(self, n: int) -> int: if n == 0 or n == 1: return 1 return (4 * n - 2) * self.catalan(n - 1) // (n + 1) def devide(self, n: int) -> int: return self.catalan(n - 2) if __name__ == '__main__': s = Solution() print(s.devide(5))
-
总结
- 假设一点固定,于是问题就被分成(之前、之后)两部分(对于划分问题来说是划分完两侧),两边都是独立的子问题,于是对于此固定点的问题可以得到总个数,再累加所有的点的总个数就是答案(这讲的就是卡特兰的递推式)