卡特兰数 斯特林数

18 篇文章 0 订阅

卡特兰数 C 2 n n − C 2 n n − 1 C_{2n}^n - C_{2n}^{n-1} C2nnC2nn1
还有常用的递推:

int main() {
	scanf("%d", &n);
	f[0] = 1, f[1] = 1;
	for(int i=2; i<=n; i++) {
		for(int j=0; j<i; j++) {
			f[i] += f[j] * f[i-j-1];
		}
	}
	printf("%d", f[n]);
    return 0;
}

貌似很多题都是第0项为1,第一项为1,第二项为2,第三项为5这样的

第一类斯特林数
S ( n , k ) = ( n − 1 ) ∗ S ( n − 1 , k ) + S ( n − 1 , k − 1 ) S(n,k) = (n-1)*S(n-1,k) + S(n-1,k-1) S(n,k)=(n1)S(n1k)+S(n1,k1)

第二类斯特林数
S ( n , k ) = k ∗ S ( n − 1 , k ) + S ( n − 1 , k − 1 ) S(n,k) = k*S(n-1,k) + S(n-1,k-1) S(n,k)=kS(n1k)+S(n1,k1)
S ( n , 1 ) = 1    ( n ≥ 1 ) S(n,1) = 1 \ \ (n \geq1 ) S(n,1)=1  (n1)
S ( n , n ) = 1 , S ( n , 0 ) = 0 S(n,n) = 1,S(n,0)=0 S(n,n)=1S(n,0)=0
对于第二类斯特林数公式的推导:
若第n个元素单独成一个集合,则有方案数 S ( n − 1 , k − 1 ) S(n-1,k-1) S(n1,k1)
若n和别的元素成一个集合,那么n可以放到k个集合中 k ∗ S ( n − 1 , k ) k*S(n-1,k) kS(n1,k)

第二类斯特林数,可以求出将n个元素的集合拆分为k个的非空集合的方案数(相当于把n个不同的小球放入m个不可区分,一模一样的盒子里)

例如,将6本不同的书分为三组,求方案数
答案是S(6,3),但我们还可以用别的方法做一下这道题
根据“先分组后分配”的思路,可以分为1,1,4;2,2,2;1,2,3三种情况
对于每种情况去重:
1,1,4 C 6 4 = C 6 2 = 15 C_6^4=C_6^2=15 C64=C62=15,分完四本书,剩下的两本自成一组(不用再除以 A 2 2 A_2^2 A22了)

如果你不信服的话,从另一个角度可以导出一样的式子: C 6 4 C 2 1 A 2 2 = C 6 4 \frac{C_6^4C_2^1}{A_2^2} = C_6^4 A22C64C21=C64(如果两个式子在数值上相同,那么这两个式子各自的组合意义可能是解决某个问题的两种思路
为什么要除以 A 2 2 A_2^2 A22?假设有A书和B书,分为两组(分组为:1,1),用 C 2 1 C_2^1 C21来算,是两种方案吗?A B 和 B A是没有差别的,所以只有一种方案。我们只是分组,而这里组和组是一样的,就像两个一模一样的盒子

2,2,2:同上, C 6 2 C 4 2 A 3 3 = 15 \frac{C_6^2C_4^2}{A_3^3} = 15 A33C62C42=15

1,2,3 C 6 3 C 3 2 = 60 C_6^3C_3^2 = 60 C63C32=60

一共90种方案,而第二类斯特林数可以递推求出:
x
1: 1
2: 1 1
3: 1 3 1
4: 1 7 6 1
5: 1 15 25 10 1
6: 1 31 90 65 15 1
7: 1 63 301 350 140 21 1
8: 1 127 966 1701 1050 266 28 1
9: 1 255 3025 7770 6951 2646 462 36 1
10: 1 511 9330 34105 42525 22827 5880 750 45 1
可得S(6,3) = 90

递推程序(要是有错的话,请在评论区指导一下我。。。)

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int MAXN = 3000 + 10;
int s[MAXN][MAXN],n;
int main() {
	scanf("%d", &n);
	for(int i=1; i<=n; i++) {
		s[i][1] = s[i][i] = 1;
		for(int j=1; j<i; j++) {
			s[i][j] = j * s[i-1][j] + s[i-1][j-1];
		}
	}
	printf("x\n");
	for(int i=1; i<=n; i++) {
		printf("%d: ",i);
		for(int j=1; j<=i; j++) {
			printf("%d ", s[i][j]);
		}
		puts("");
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值