ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

卡特兰数

2022-08-21 18:04:01  阅读:138  来源: 互联网

标签:int long 括号 序列 2n 卡特兰


概念:

卡特兰数并不是一个确定的数,而是一类数,是组合数学中一种常出现于各种计数问题中的数列。它没有一个明确的定义,但可以通过一些模型得出关于卡特兰数的很多信息,下面介绍几个这样的问题。

问题/模型:

(一)路径问题

image

给定 \(n*n\) 的网格(上图是一个 \(6*6\) 的网格),初始你在左下角的格点 \((0,0)\),每次你只能往右或者往上走一格,且不能穿过对角线(图中的蓝线),走到 \((n,n)\) 的方案数记作卡特兰数 \(C(n)\)。

考虑离开 \((0,0)\) 之后,第一次走到对角线上的位置为 \((i+1,i+1)\),将从 \((0,0)\) 走到 \((i+1,i+1)\) 的过程 称为第一阶段,从 \((i+1,i+1)\) 走到 \((n,n)\) 的过程称为第二阶段,那么第二阶段的方案数显然是 \(C(n-(i+1))=C(n-i-1)\);第一阶段的过程肯定是先往右边走到 \((1,0)\),然后在不越过 \(y=x-1\) (即图中红线)的情况下到达 \((i+1,i)\),然后往上走一步到达 \((i+1,i+1)\),所以阶段 1 的方案数为 \(C(i)\)。所以总方案数为 \(C(n)=\sum_{i = 0}^{n-1}C(i) * C(n - i - 1)\)

可以发现,在卡特兰数的问题中,一般常有两种选择,需要保证其中一种选择的数量在任意前缀中不大于另一种选择的数量。将上面的例子代入,可以发现两种选择分别为向上走或向右走,需要保证向上走的步数不大于向右走的步数。因此,我们除了可以根据它推出递推式以外,还可以得到一个通项公式。

考虑一共走 \(2n\) 步,如果y要走到终点,需要向上走 \(n\)步,向右走 \(n\) 步,只要向上走的步数比向右走的步数多一步,就会跨过蓝线,因此只要将所有的移动方式减去非法的移动方式,即可得到答案。

\[C(n) = {2n \choose n} - {2n \choose n + 1} \]

化简,得到另一个通项公式:

\[C(n) = \frac{2n \choose n}{(n + 1)} \]

整理,又可以得到另一个递推式:

\[C(n) = \frac{2 * (2n - 1)}{n + 1} * C(n - 1) \]

(二)合法括号序列

\(n\) 对括号形成的合法放置方案为 \(C(n)\)。如果 \(S\) 是一个合法方案,那么 \((S)\) 也是合法方案;如果 \(S1\) 和 \(S2\) 都是合法方案,那么 \(S1+S2\) 也是合法方案,此处的 \(+\) 表示两个括号序列接在一起。

两种选择分别为在当前位置上放左括号或右括号,要求保证在任何前缀中右括号的数量不大于左括号的数量。

(三)进出栈

\(n\) 个数依次进栈,出栈序列的方案数为 \(C(n)\)。

两种选择分别为出栈和进栈,要求在操作的任何前缀保证出栈数量不大于进栈数量。

(四)凸多边形三角形划分

凸 \(n+2\) 边形在端点之间连接一些互不相交的线段,将多边形划分成 \(n\) 个三角形,划分方案的数量为 \(C(n)\)。

(五)二叉树种类

\(n\) 个节点的二叉树的种数为 \(C(n)\)。

(四)(五)两个问题,类比(一)路径问题即可。

总结:

以卡特兰数为背景的题目的一大特点就是常常有两种选择,并需要保证在前缀中一种选择的数量不大于另一种选择(emmm也许还有多种选择的题目,只是我们遇到过23333)。同时,以卡特兰数为背景的题目也可以用动态规划来解决?想一想,为什么?观察一下卡特兰数的递推式,不就是很像动态规划吗(

习题:

(一)HDU 5184 Brackets

我们给出“正则括号”序列的归纳定义:
●空序列是正则括号序列,
●如果s是正则括号序列,则 \((s)\) 是正则括号序列,
●如果 \(a\) 和 \(b\) 是正则括号序列,则 \(ab\) 是正则括号序列。
●没有其他序列是常规括号序列
例如,以下所有字符序列都是常规括号序列:
\((),(()),()(),()())\)
而以下字符序列不是:
\((,),(((),()),(()\)
现在我们要构造一个长度的常规括号序列 \(n\),当前面已经给出了几个括号时,我们可以得到多少个常规括号序列。

首先判断原序列是不是合法,如果不合法,即出现了右括号数量比左括号多的情况或 \(n\) 为奇数,直接输出 \(0\)。否则,可以统计出需要添加多少个右括号。将给出的括号序列补成一个正则括号后,可能序列长度还不到 \(n\),因此还要将剩下的位置补全。设将原序列补充为正则括号后的长度为 \(s\),则左括号和右括号分别还需要添加 \(\frac{n - s}{2}\) 个。

设将原序列补充为一个正则括号所需要的右括号的个数为 \(a\),则右括号总共需要添加 \(a + \frac{n - s}{2}\) 个,左括号总共需要添加 \(\frac{n - s}{2}\) 个。

让我们结合一下上面提到的模型(二)合法括号序列,容易发现,所要添加的总括号长度是 \(a + n - s\) 答案就是:

\[ans = {{{a + n - s} \choose {a + \frac{n - s}{2}}} - {{a + n - s} \choose {a + \frac{n - s}{2} + 1}} } \]

组合数要取模,逆元即可。

点击查看代码
#include<iostream>
#include<string>
#include<stack>
#include<string.h>
#define int long long  
using namespace std;
const int MOD = 1e9 + 7;
const int MAXN = 1e6 + 5+1e5+1e4;
int inv[MAXN + 5];
char s[MAXN];
int f[MAXN + 5];
int qpow(int a,int n){
	int ret = 1;
	while(n){
		if(n & 1){
			ret = (long long)ret * a % MOD;
		}
		n >>=1;
		a = (long long)a * a % MOD;
	}
	return ret;
}

int c(int n,int m){
	if(m == -1)return 0;
	return (long long)f[n] * inv[m] % MOD * inv[n - m] % MOD;
} 
int a,b,n;
signed main(){
	//ios::sync_with_stdio(false);
	//cin.tie(0);
	//cout.tie(0);
	f[0] = 1;
	for(int i = 1;i <= MAXN; i++){
		f[i] = (long long)f[i - 1] * i % MOD;
	}
	inv[MAXN] = qpow(f[MAXN],MOD - 2);
	for(int i = MAXN - 1; i >= 0; i--)
	{
		inv[i] = (long long)inv[i + 1] * (i + 1) % MOD;
	}
	while(~scanf("%d%s",&n,s)){
		int st = 0;
		a = b = 0;
		int siz = strlen(s);
		for(int i = 0; i < siz ; i++){
			if(s[i] == '(')++st;
			if(s[i] == ')'){
				if(st)st--;
				else a++;
			}
		}
		b += st;
		int k = n - siz - b;
		if(a > 0 ||(n%2 || k<0)){
			cout << "0\n";
			continue;
		}
		a += k / 2;
		b += k / 2;
		cout << (c(a + b,a) - c(a + b,a - 1) + MOD) % MOD<< "\n";
		for(int i = 0; i < siz; i++){
			s[i] = '\0';
		}
	}

}

(二)P3200 [HNOI2009]有趣的数列(?)

题目描述
我们称一个长度为 \(2n\) 的数列是有趣的,当且仅当该数列满足以下三个条件:
●它是从 \(1 \sim 2n\) 共 \(2n\) 个整数的一个排列 \(\{a_n\}_{n=1}^{2n}\)
●所有的奇数项满足 \(a_1<a_3< \dots < a_{2n-1}\) 所有的偶数项满足 \(a_2<a_4< \dots <a_{2n}\)
●任意相邻的两项 \(a_{2i-1}\) 与 \(a_{2i}\) 满足:\(a_{2i-1}<a_{2i}\)
对于给定的 \(n\),请求出有多少个不同的长度为 \(2n\) 的有趣的数列。
因为最后的答案可能很大,所以只要求输出答案对 \(p\) 取模。

注意到,由于所有的奇数项都是递增的,且任意相邻的两项 \(a_{2i-1}\) 与 \(a_{2i}\) 满足:\(a_{2i-1}<a_{2i}\),容易得到,所有偶数项上的数大于前面的所有数,再推导一下,所有偶数项的数的大小至少等于其下标。

在数列的前 \(a\) 项中,下标为偶数的项一共有 \(n/2\) 项。所以前 \(i\) 位所放置的偶数不能超过 \(n / 2\) 个,结合卡特兰数即可解。

标签:int,long,括号,序列,2n,卡特兰
来源: https://www.cnblogs.com/CZ-9/p/16591148.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有