『笔记』数学数论(七)
简介
卡特兰数(Catalan),又称明安图数,是组合数学中一个常出现于各种计数问题的数列。
对应序列为:
\(1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, \cdots\)
满足
其中 \(C_0=1,C_1=1\)
性质
注:为避免符号重复本文所有公式将以 \(M_n\) 表示卡特兰数,即明安图数的拼音首字母。
符合通项公式:
那么可以推出如下公式:
基本原理
先来看一个问题:
对于 \(n\) 对括号,求共有多少种合法的匹配方案数。
其中括号的合法匹配方式为:一个左括号对应一个右括号,且左括号必须要在右括号前面出现。
这里我们用 +1
表示 “ \((\) ” ,用 -1
表示 “ \()\) ”。
那么显然,对于一组合法的括号序列,该序列的前缀和必然大于或等于 \(0\) ;相反,若该括号序列不合法,其前缀和必然存在小于 \(0\) 的情况。
首先给出一个序列:
()(())()
该序列可以表示为
+1 -1 +1 +1 -1 -1 +1 -1
显然这是一个非法序列。
对于像这样的一个非法序列,找到第一个前缀和小于 \(0\) 的括号,并对该前缀中的每一个数进行取反。
上述例子便可以得到
-1 +1 +1 +1 -1 +1 +1 -1
此时该序列中共有 \(3+1+1=5\) 个 +1
和 \(4-1=3\) 个 -1
。不难看出,第一个小于 \(0\) 的前缀和必定为 \(-1\) ,即 \(-1\) 比 \(+1\) 多一个,取反后则 \(-1\) 比 \(+1\) 少一个。这样总体上看,由于二者数量原本相同,所以 \(+1\) 必定变为 \(n+1\) 个,\(-1\) 则变为 \(n-1\) 个。
由此,该结论可以推广:
对于 \(n\) 对括号的每种非法匹配序列 \(A\),一定会有一个含有 \(n+1\) 个 \(+1\) 和 \(n-1\) 个 \(-1\) 的序列 \(B\) 与其一一对应。
而序列 \(B\) 的数量可以通过 \(C_{2n}^{n+1}\) 计算,即非法序列的数量为 \(C_{2n}^{n+1}\)。
而序列的总数量为 \(C_{2n}^n\) (从 \(2n\) 个位置中选择 \(n\) 个位置放左括号,不考虑先后顺序),则合法的匹配序列数量为:
由此便推导出了卡特兰数的通项公式。
应用
P1044 [NOIP2003 普及组] 栈
直接由基本原理可以推出:
公式 1
代码:
/*
Name: P1044 [NOIP2003 普及组] 栈
Solution:
By Frather_
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define int long long
using namespace std;
/*==================================================快读*/
inline int read()
{
int X = 0, F = 1;
char CH = getchar();
while (CH < '0' || CH > '9')
{
if (CH == '-')
F = -1;
CH = getchar();
}
while (CH >= '0' && CH <= '9')
{
X = (X << 3) + (X << 1) + (CH ^ 48);
CH = getchar();
}
return X * F;
}
/*===============================================定义变量*/
const int _ = 110;
int n;
int h[_];
/*=============================================自定义函数*/
/*=================================================主函数*/
signed main()
{
h[0] = 1;
h[1] = 1;
n = read();
for (int i = 2; i <= n; i++)
h[i] += h[i - 1] * (4 * i - 2) / (i + 1);
printf("%lld\n", h[n]);
return 0;
}
公式 2
代码:
/*
Name: P1044 [NOIP2003 普及组] 栈
Solution:
By Frather_
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define int long long
using namespace std;
/*==================================================快读*/
inline int read()
{
int X = 0, F = 1;
char CH = getchar();
while (CH < '0' || CH > '9')
{
if (CH == '-')
F = -1;
CH = getchar();
}
while (CH >= '0' && CH <= '9')
{
X = (X << 3) + (X << 1) + (CH ^ 48);
CH = getchar();
}
return X * F;
}
/*===============================================定义变量*/
const int _ = 110;
int n;
int h[_];
/*=============================================自定义函数*/
/*=================================================主函数*/
signed main()
{
h[0] = 1;
h[1] = 1;
n = read();
for (int i = 2; i <= n; i++)
for (int j = 0; j < i; j++)
h[i] += h[j] * h[i - j - 1];
printf("%lld\n", h[n]);
return 0;
}
公式 3
其中
并且规定
代码:
/*
Name: P1044 [NOIP2003 普及组] 栈
Solution:
By Frather_
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define int long long
using namespace std;
/*==================================================快读*/
inline int read()
{
int X = 0, F = 1;
char CH = getchar();
while (CH < '0' || CH > '9')
{
if (CH == '-')
F = -1;
CH = getchar();
}
while (CH >= '0' && CH <= '9')
{
X = (X << 3) + (X << 1) + (CH ^ 48);
CH = getchar();
}
return X * F;
}
/*===============================================定义变量*/
const int _ = 110;
int n;
int h[_ << 1][_];
/*=============================================自定义函数*/
/*=================================================主函数*/
signed main()
{
n = read();
for (int i = 1; i <= n << 1; i++)
{
h[i][0] = 1;
h[i][i] = 1;
for (int j = 1; j < i; j++)
{
h[i][j] = h[i - 1][j] + h[i - 1][j - 1];
}
}
printf("%lld\n", h[n << 1][n] / (n + 1));
return 0;
}
公式 4
代码:
/*
Name: P1044 [NOIP2003 普及组] 栈
Solution:
By Frather_
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define int long long
using namespace std;
/*==================================================快读*/
inline int read()
{
int X = 0, F = 1;
char CH = getchar();
while (CH < '0' || CH > '9')
{
if (CH == '-')
F = -1;
CH = getchar();
}
while (CH >= '0' && CH <= '9')
{
X = (X << 3) + (X << 1) + (CH ^ 48);
CH = getchar();
}
return X * F;
}
/*===============================================定义变量*/
const int _ = 110;
int n;
int h[_ << 1][_];
/*=============================================自定义函数*/
/*=================================================主函数*/
signed main()
{
n = read();
for (int i = 1; i <= n << 1; i++)
{
h[i][0] = 1;
h[i][i] = 1;
for (int j = 1; j < i; j++)
h[i][j] = h[i - 1][j] + h[i - 1][j - 1];
}
printf("%lld\n", h[n << 1][n] - h[n << 1][n - 1]);
return 0;
}
如果还有其他公式欢迎联系博主!(博主最多推四个了。。)