题面
给定一个1*(2n)的矩阵。现让你放入一样多的红色算筹和黑色算筹,使对于所有的i(1<=i<=2n),使第1~i格中红色算筹个数大于等于黑色算筹
(同P1044栈)
思路
记红为R
,黑为B
,输入n时的结果为Cn
画几个之后,发现这个问题具有子结构。
如n-1的情况可以通过在后面补一个RB
构成n的情况。
但若要推导递推公式,需要更确定的子结构划分。
注意,不能通过删去n情况的末尾两个形成n-1的情况,因为不知道n-1内是不是恰好有n-1个R和B
如情况RRRRR...BBBBB
(n个红,n个黑),结果是Cn,但删去末尾两个后得不到Cn-1
注意到最末的格子必为黑色,而且它前面未必是红,所以在划分的时候不应包含它。(完整的RB+完整的RB=完整的RB,完整的RB才可应用Ci递推)
现在序列中多余了一个红色的元素,也不应包含(和末尾B配对的R)。这样,剩下的部分就是完整部分。
配对RB列的种类数是Ci ,其中i是列长度
这种R有n-1个可能存在的位置,即1 ~ n-1,不同的情况符合加法原理
前面的RB列长0时,后面RB列长n-1(实际长度为2倍,这里说的是输入为相应规模的子问题)
以此类推
所以
a
n
s
=
C
0
C
n
−
1
+
C
1
C
n
−
2
+
.
.
.
+
C
n
−
2
C
1
+
C
n
−
1
C
0
ans=C_0C_{n-1}+C_1C_{n-2}+...+C_{n-2}C_1+C_{n-1}C_0
ans=C0Cn−1+C1Cn−2+...+Cn−2C1+Cn−1C0
这就是卡塔兰数定义
从小到大打表即可
这个过程和括号配对种数问题,栈出列种数问题是等价的,在寻找和末尾B配对的R时就应当发现了。
代码
#include <stdio.h>
#include<iostream>
using namespace std;
int C[105];
int main()
{
ios::sync_with_stdio(false);
C[0] = 1;C[1] = 1;C[2] = 2;
for (int i = 3; i <=100 ; i++)//打表
{
for (int j = 0, k = i - 1;j <= i - 1;j++, k--) {
C[i] += C[j] * C[k] % 100;
}
C[i] %= 100;//分步取模
}
int n;
cin >> n;
cout << C[n];
return 0;
}