C/C++递归递推算法:[走楼梯(斐波那契数列),出栈序列有几种(Catalan 卡特兰数)]

走楼梯:

        楼梯有 N 阶,上楼可以一步上一阶,也可以一步上二阶。

        编一个程序,计算共有多少种不同的走法。

递归:N阶段楼梯的走法 = (N-1)阶楼梯的走法+(N-2)阶楼梯的走法

算法理解:(N - 1)阶楼梯走法再走一阶楼梯到达第N阶,(N - 2)阶楼梯再直接走二阶楼梯到达第N阶(注意):(N - 2)阶楼梯再走两次一阶楼梯这种走法已经包含在了(N - 1)阶楼梯里,因此舍弃掉这种走法。

递归出口:

1、走到1阶楼梯时,剩余一种走法。

2、走到0阶楼梯时,定义为一种走法。(注意)这里需要结合实际情况讨论:当n = 2时,根据递归公式,2阶楼梯走法=1阶楼梯走法+0阶楼梯走法,(2 = 1 + 1)因此0阶楼梯需要定义为1种走法。

#include<iostream>

using namespace std;

int ladder;
int sum_Ladder(int ladder) {

	if (ladder == 0) {
		return 1;
	}
	if (ladder == 1) {
		return 1;
	}
	return  sum_Ladder(ladder - 1) + sum_Ladder(ladder - 2);

}
int main() {
	cin >> ladder;
	cout <<sum_Ladder(ladder);
}

斐波那契数列:

        斐波那契数列:F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)

        例:0、1、1、2、3、5、8、13、21、34、.....

        这种问题的答案符合斐波那契数列,因此也可以通过编写C++斐波那契数列解决问题。

#include<iostream>

using namespace std;

int ladder;

int main() {
	long long a[501];
	a[1] = 1;
	a[2] = 2;
	for (int i = 3; i < 501; i++) {
		a[i] = a[i - 1] + a[i - 2];
	}
	cin >> ladder;//请输入小于500的数字
	cout << a[ladder];

}

 出栈序列有几种:

题目描述:

        n 个元素进栈序列为:1,2,3,4,...,n,则有多少种出栈序列。

目标:求出有几种合法序列
思路:总序列减去非法序列
第一步:找到非法序列的条件       

       非法序列就是非法出栈。我们可以这样表示: 进栈代表1,出栈代表-1,当在某一个位置上的前缀和为负数时候,这一位置是非法的 → 这一序列也就是非法的

        例如:1,2,3的一个非法出栈序列为:+1, -1, -1, + 1, -1, +1。此时在第三个位置为-1,前缀和等于(+1) +(-1) +(-1)= -1 < 0。此时3号位置为第一个前缀和小于0的非法位置。

        这样就找到了非法序列,所有的非法序列都满足这一规律。接下来第二步就是求出非法序列的个数。

第二步:求出非法序列的个数

        首先我们要知道当有n个数时,所有的元素都要进栈和出栈,那么所有的出+1和-1个数加起来等于2n

        对于所有的非法序列,我们先在第一步的非法位置和它之前的所有位置都进行取反操作。(例如在第一步的三个元素的出栈序列中,找到的第三个位置前的元素都进行取反,这样这个序列就变成了4个+1和2个-1),由于整体序列为n个+1,n个-1,经过这样变换后,整个数列变成了(n+1)个+1和(n - 1)个-1的数列。 这里我们将原非法序列设为A,经过这种变换后的序列设为B,一个A只对应一个B(证明略,可以自己画图理解)。

        因此我们想要知道因此非法序列A的总数,就只需要知道变种后的B序列的总数,由上一段可知,B的总数就是在2n个数里面选择(n+1)个数变为+1,B的总数为C_{2n}^{n+1},相应的非法序列A的总数也就是C_{2n}^{n+1},所有的出栈序列减去非法的出栈序列就等于合法的出栈序列:C_{2n}^{n} - C_{2n}^{n+1}=\frac{C_{2n}^{n}}{n+1}这就是卡特兰数的通项公式。

是不是忘记了之前学过的排列组合?

A_{m}^{n}是排列,有顺序,表示m是起点,从m中按顺序取n个数

A_{m}^{n} = m(m-1)(m-2)(m-3) ... (m-n+1)

例如:A_{8}^{4} = 8x7x6x5

C_{m}^{n}是组合,无顺序,表示从m中选出n个数

C_{m}^{n} = \frac{A_{m}^{n}}{A_{n}^{n}} (其中除以A_{n}^{n}是把排列带来的顺序给去除掉)

例如:C_{8}^{4} =\tfrac{A_{8}^{4}}{A_{4}^{4}} =(8x7x6x5)/(4x3x2x1)

编程实现:

直接计算版本(不推荐):
#include<iostream>

using namespace std;

void Catalan(long long &num) {
	long long C_up = 1;
	int C_down = 1;
	for (int i = num * 2; i > num; i--) {
		C_up = C_up * i;
	}
	for (int i = num; i > 0; i--) {
		C_down = C_down * i;
	}
	long long ans = C_up / C_down / (num + 1);
	cout << ans;
}

int main() {
	long long num;
	cin >> num;
	Catalan(num);
}
递推公式:

这个代码在数字量过大时将long long无法存储,可以采用高精度算法(偷懒一下,有时间再写),或递推公式。

下面采用递推公式:

推导:

 

 递推代码:

#include<iostream>

using namespace std;

long long ctl[33];
void Catalan() {
	ctl[0] = 1;
	ctl[1] = 1;
	for (int i = 1; i < 32; i++) {
		ctl[i + 1] = ctl[i] * (4 * i + 2) / (i + 2);
	}
}
int main() {
	long long num;
	cin >> num;
	Catalan();
	cout << ctl[num];
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值