递推总结

首先:

什么是递推

递推

中文名 外文名
递推 The Recursive

定义

“递推是按照一定的规律来计算序列中的每个项,通常是通过计算前面的一些项来得出序列中的指定项的值。其思想是把一个复杂的庞大的计算过程转化为简单过程的多次重复,该算法利用了计算机速度快和不知疲倦的机器特点。”

“所谓递推,是指从已知的初始条件出发,依据某种递推关系,逐次推出所要求的各中间结果及最后结果。其中初始条件或是问题本身已经给定,或是通过对问题的分析与化简后确定。
已知条件出发逐步推到问题结果,此种方法叫顺推
问题出发逐步推到已知条件,此种方法叫逆推
无论顺推还是逆推,其关键是要找到递推式。这种处理问题的方法能使复杂运算化为若干步重复的简单运算,充分发挥出计算机擅长于重复处理的特点。
递推法是一种重要的数学方法,在数学的各个领域中都有广泛的运用,也是计算机用于数值计算的一个重要算法。
递推算法的首要问题是得到相邻的数据项间的关系(即递推关系)。递推算法避开了求通项公式的麻烦,把一个复杂的问题的求解,分解成了连续若干步简单运算。一般说来,可以将递推算法看成是一种特殊迭代算法。”

总而言之,递推的核心思想是找出

递推式

言归正传,五种递推模型如下:

五种递推模型

斐波拉契 Fibonacci

斐波那契数列(Fibonacci sequence),又称黄金分割数列、因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”。

数列为:1、1、2、3、5、8、13、21、34、……

在数学上,斐波那契数列以如下被以递推的方法定义:*F(1)=1,F(2)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 3,n ∈ N)**在现代物理、准晶体结构、化学等领域,斐波纳契数列都有直接的应用,为此,美国数学会从 1963 年起出版了以《斐波纳契数列季刊》为名的一份数学杂志,用于专门刊载这方面的研究成果。

数列已经给出,递推式即为F(n)=F(n - 1)+F(n - 2)(n ≥ 3,n ∈ N*) 边界条件为F(1)=1,F(2)=1

代码如下 :

#include<cstdio>
#include<cmath>
int main()
{
	freopen("rabbit.in","r",stdin);//注意看题!
	freopen("rabbit.out","w",stdout);
	int n;
	long long f[100005];
	f[1]=1;
	f[2]=1;
	scanf("%d",&n);
	for(int i=3;i<=n;i++)
	{
		f[i]=f[i-1]+f[i-2];
	}
	printf("%d",f[n]);
	return 0;
 } 

汉诺塔

法国数学家爱德华·卢卡斯曾编写过一个印度的古老传说:在世界中心贝拿勒斯(在印度北部)的圣庙里,一块黄铜板上插着三根宝石针。印度教的主神梵天在创造世界的时候,在其中一根针上从下到上地穿好了由大到小的64片金片,这就是所谓的汉诺塔。不论白天黑夜,总有一个僧侣在按照下面的法则移动这些金片:一次只移动一片,不管在哪根针上,小片必须在大片上面。僧侣们预言,当所有的金片都从梵天穿好的那根针上移到另外一根针上时,世界就将在一声霹雳中消灭,而梵塔、庙宇和众生也都将同归于尽。

不管这个传说的可信度有多大,如果考虑一下把64片金片,由一根针上移到另一根针上,并且始终保持上小下大的顺序。这需要多少次移动呢?这里需要递归的方法。假设有n片,移动次数是f(n).显然f(1)=1,f(2)=3,f(3)=7,且f(k+1)=2*f(k)+1。此后不难证明f(n)=2^n-1。n=64时,
假如每秒钟一次,共需多长时间呢?一个平年365天有31536000 秒,闰年366天有31622400秒,平均每年31556952秒,计算一下:

18446744073709551615秒这表明移完这些金片需要5845.54亿年以上,而地球存在至今不过45亿年,太阳系的预期寿命据说也就是数百亿年。真的过了5845.54亿年,不说太阳系和银河系,至少地球上的一切生命,连同梵塔、庙宇等,都早已经灰飞烟灭。

数列模拟后可以得知 : 1,3,7,15,31,63… …
递推式为 H[i]=H[i-1]2+1 边界条件为H[1]=1*
代码如下:

#include <cstdio>
#include <iostream>
int main() {
 long long n,H[68];
 scanf("%lld",&n);
 H[1]=1;
 for(int i=2;i<=n;i++) {
 	H[i]=H[i-1]*2+1;
 }
 printf("%lld",H[n]);
 return 0;
}

分割平面

这道题可以通过图来算
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c8GuTFFk-1592638665357)(http://media.openjudge.cn/images/upload/1523337982.jpg “乱动什么鼠标”)]
看看就知道 数列为 2,4,8,14

数列已经给出,递推式即为a[i]=a[i-1]+2*(i-1); 边界条件为a[1]=2;

代码如下:

#include <cstdio>

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

Catalan

卡特兰数又称卡塔兰数,英文名Catalan number,是组合数学中一个常见数列,前几项为(从第零项开始) : 1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012, 742900, 2674440, 9694845, 35357670, 129644790, 477638700, 1767263190, 6564120420, 24466267020, 91482563640, 343059613650, 1289904147324, 4861946401452… …

这里因为表示按照上述规则划分的三角形区域个数,那么我们随便选一条多边形的一条边作为基边,那么

 再在剩余的n-1个点中选一个点,我们把所选的一条边的两点分别与所选的那一点连接起来,那么多边形被划

 分成3部分,一部分有k+1条边,一部分有3条边,另一部分有n-k+1条边,那么这样就划分成了子问题了,所

 以按照这个思路可以证明递推式成立。

已经给出了数列及推导,递推式边界条件为 :

a[0] = 0, a[1] = 0, a[2] = 1, a[3] = 1;
	for(int i = 4; i <= n; i++) { 
		for(int j = 2; j <= i - 1; j++) 
			a[i] += (a[j] * a[i - j + 1]);
	}

代码如下 :

#include <cstdio>
const int MAXN = 45;
long long a[MAXN];
int main() {
	int n;
	scanf ("%d", &n);
	a[0] = 0, a[1] = 0, a[2] = 1, a[3] = 1;
	for(int i = 4; i <= n; i++) { 
		for(int j = 2; j <= i - 1; j++) 
			a[i] += (a[j] * a[i - j + 1]);
	}
	printf("%lld\n", a[n]);
	return 0;
} 

第二类stirling

第二类Stirling数,其表示将n个不同的元素分成m个集合的方案数。

求法

一般有两种求法。
递推:
S(n,m)=S(n−1,m−1)+mS(n−1,m)

即讨论第一个球是否单独在一个盒子里面。
如果不独占一盒,那么把这个球放进任一个盒子,这个盒子就相当于与其他的盒子不同,那么在乘答案的时候就要多乘一个m.

容斥原理:
S(n,m)=1m!∑k=0m(−1)kC(m,k)(m−k)n

即枚举空盒的个数,剩下的随意放置,由于盒子是相同的最后要除以m!
注意到这个式子是一个卷积,所以可以在O(nlogn)内求出S(n,0),S(n,1)…

性质=0kS(k,i)×i!×C(n,i)nk=

只有一个公式

n k = i = 0 k S ( k , i ) × i ! × C ( n , i ) n^k=\sum_{i=0}^k S(k,i)×i!×C(n,i) nk=i=0kS(k,i)×i!×C(n,i)

很好理解,左边就是k个球可以任意放置在n个盒子里。
右边就是枚举非空盒子的数量i,那么把k个球放在i个盒子(盒子不同,需要乘上一个i!)里面再乘上选出i个非空盒子的方案数。

代码如下:

#include <cstdio>

const int MAXN = 25;
long long a[MAXN][MAXN];

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