卡特兰数为什么是这样的

· · 算法·理论

背景

卡特兰数第 n 项记为 H_n,根据学过的知识,H_n = C_{2n} ^ n - C_{2n} ^ {n - 1}

可是这是怎么推出来的呢?

思考

一个二维表(如下),从 (0, 0) 点,每一步只能向上或向右走一格,走到 (n, n) 点的方案数是多少?

对于本蒟蒻来说,首先想到的就是动态规划了。

具体来说,dp_{i, j} 表示从 (0, 0)(i, j) 的方案数是多少。

对于每一 (i, j) 来说,它要么是从下面走过来的,要么是从左边走过来的。所以状态转移方程:dp_{i, j} \gets dp_{i - 1, j} + dp_{i, j - 1}

那很明显了,初始化所有 dp_{i, 0}dp_{0, j}dp_{0, 0}

显然,dp_{0, 0} = 1,因为刚开始就在 (0, 0) 点。

同样,dp_{i, 0} = 1dp_{0, j} = 1

好的,我们现在换一种思路:

(0, 0) 开始,每次操作可以抽象为向右 \rightarrow 和向上 \uparrow 操作。

那么,从 (0, 0) 走到 (n, n),就需要有 n\rightarrow 操作和 n\uparrow 操作。

那么,这个问题就抽象为,2n 个位置,选出其中 n\rightarrown\uparrow 的方案数

这个问题的答案明显就是 C_{2n} ^ n。因为 C_{2n} ^ n 的定义就是这个。

好的,恭喜你成功的看到了这里,打败了 50\% 的人,扣个 1。

现在这个问题变了一下,从 (n, n)(0, 0) 拉一条直线,这条直线一下的区域(不包括这条线上的点)是危险区域(红色区域,如下图),不可以去那里玩。请问这样有多少种路径从 (0, 0)(n, n) 呢?

组合数学,正难则反。先不考虑有红色区域,方案数就是 C_{2n} ^ n

那涉足红色区域的方案数又有多少呢?

当某个路径第一次涉足红色区域时,一定是从某个点 (k, k) 向右走到的点。换言之,这个点是从左边走来,不可能是从下面走来。

我们把这个点之后的所有操作全部取反(\rightarrow\uparrow\uparrow\rightarrow)。

如上图,J 点是第一次涉足的位置,蓝线是原本的路径,绿色线是取反后的路径。

一定的,取反后的路径的目的地是 (n - 1, n + 1)

简单解释一下为什么:

第一次涉足下方的点一定是第一次出现 \rightarrow\uparrow 的点多一个的位置,所以原本路径后面的 \rightarrow 会比 \uparrow 少一。所以取反之后,整个路径里 \rightarrow 会比 \uparrow 多二,所以会出现高度减一而长度加一的情况。

而每一个涉足下面区域的路径,都一定对应一个取反后的路径到达 (n + 1, n - 1) 的路径。

所以所有涉足下面区域的路径,方案数为 C_{2n} ^ {n - 1}

所以所有合法的方案数就是 C_{2n} ^ n - C_{2n} ^ {n - 1}

并且这个问题是卡特兰数的一个经典问题。

好的,短暂的回顾一下上面的推导过程。

拓展

好的,恭喜你成功的看到了这里,打败了 75\% 的人,扣个 2。

卡特兰数还有其他的应用,例如下面这个经典问题:

你有 n 个 “(” 和 n 个 “)”,你能组成多少种合法的括号序列?

(合法:指任意前缀中,左括号的数量都不少于右括号的数量,并且最终左右括号数量相等)。

先别管“合法”,先考虑 n 个 “(” 和 n 个 “)”有多少种方案。

答案很明显了,C_{2n} ^ n

好的,现在考虑不合法的。

例如这一串:

((((()))))))((

第一次出现问题的地方是第十一个字符,其 1 \sim 11 的序“)”比“(”多一个。

从第十二个字符开始,每一个括号反转(同格点图,“(”变“)”,“)”变“)”)。

序列变为:((((())))))\color{red}())

其中,反转之后,“)”就会有 n + 1 个,比“(”多两个。

而每一个不合法序列都可以对应一个 n + 1 个 “)”,n - 1 个“(”的序列,所以合法的序列有 C_{2n} ^ n - C_{2n} ^ {n - 1} 个。

好的,恭喜你成功的看到了这里,打败了 83\% 的人,扣个 5。

你可能会疑惑,1、2 之后为什么是 5?

来,总结一下它俩都有什么共同点?

没错,都是抽象操作后去找零界点然后反转。

那相信聪明的你也知道了这个问题:

假设有一个无穷大的栈,我们进行 n 次入栈(Push)操作和 n 次出栈(Pop)操作。

问题是:有多少种不同的操作顺序,使得在操作的任何时刻,都不会因为试图从一个空栈中弹出元素而导致操作失败?

这种合法的操作序列的数量,就是第 n 个卡特兰数 H_n

提示:把入栈相乘 \rightarrow,出栈想成 \uparrow,其中每次操作变成一个序列。

每个前缀里,\rightarrow 不能少于 \uparrow,否则就像涉足了格点图里的红色区域,这样的序列需要扣除,具体方法就是括号匹配问题了。

好的,恭喜你成功的看到了这里,打败了 90\% 的人,扣个 14。

没错!这是卡特兰数列!

总结

今天突然想到了这个东西,写下来记录一下,也顺便给一些不懂的人提一下卡特兰数的推导。

其实卡特兰数还有二叉树的序列问题,时间关系,后面再讲。