一对兔子从出生后第三个月开始,每月生一对小兔子。小兔子到第三个月又开始生下一代小兔子。假若兔子只生不死,一月份抱来一对刚出生的小兔子,问1年中每个月各有多少对兔子。(不死神兔)

我们现在重述一下问题:

一对兔子从出生后第三个月开始,每月生一对小兔子。小兔子到第三个月又开始生下一代小兔子。假若兔子只生不死,一月份抱来一对刚出生的小兔子,问1年中每个月各有多少对兔子。

我们首先枚举一下前几个月

我们发现:

第一个月有一对兔子

第二个月有一对兔子

第三个月有两对兔子

第四个月有三对兔子

第五个月有五对兔子

……

这其中的规律你发现了么?

是的没错,除了第一个月和第二个月只有一个以外,其他的月份,当前这一个月的兔子的对数等于前两个月兔子的对数的和,有了这个思想,我们就可以利用代码实现了。

我们首先采用递归法来实现

递归方法如下:

#include 

using namespace std;

int dg(int n) {
	if (n == 1 || n == 2)
		return 1;
	else
		return dg(n - 1) + dg(n - 2);
}

int main() {
	int n;

	for(int i=1;i<13;++i)
	cout<

但是我们仔细思考,这个递归的方法貌似有些计算是重复计算了,比如说当计算dg(5)的时候,我们需要计算dg(4),dg(3),dg(2),dg(1),而计算dg(4)的时候,计算了dg(3),dg(2),dg(1),这里的dg(3),dg(2),dg(1)就是重复计算的。

那么我们是否可以简化这个计算呢,很明显这题可以转化成为一个简单的动态规划(动态规划就是带记忆的递归)。

那么我们可以开一个数组,取名为dp,数组的长度为13,也就是dp[13],由于数组下标是从0开始的,但是由于人类的习惯,所以我选择开一个长度为13的数组,这样下标就能到12,也就是代表十二个月。

那么我们定义一下每个数组里面的值代表的是该月兔子的数量(以下标代替月份)

那么我们就可以得到代码如下:

#include 
using namespace std;
const int N=13;
int dp[N];

int main() {
	int n;
	dp[1]=1;
	dp[2]=1;
	cout<

那么我们能否再继续优化呢

我们可以思考一下

每个月的兔子的数量只能前两个月的兔子的数量有关

我们假设第一个月的兔子的数量为first,第二个月的兔子的数量为second,第三个月的兔子的数量third。然后third=first+second,然后到了第四个月,第四个月的数量是第二个月的数量加上第三个月的兔子的数量,这时候我们可以把第二个月想象成为first,第三个月想象成为second,那么第四个月就等于first+second,那么我们怎么进行转化呢,其实我们可以进行覆盖,当第三个月的数量等于第一个月的数量加上第二个月的数量之后,我们这时候可以将second的值赋值给first,third的值赋值给second,这时候third=first+second,也就实现了滚动求值,我们再可以每一次获得到second的值之后将second输出即可

整个代码如下所示:

#include 
using namespace std;
int main() {

	int first=1,second=1,third;
	cout<

至此,整个代码就优化完毕,我们可以回顾一下整个流程,我们先进行了递归暴力求解,接下来就是利用dp来优化时间复杂度,最后再利用滚动数组来优化空间复杂度。

你可能感兴趣的:(算法,c++,算法,数据结构)