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

我们现在重述一下问题:

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

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

我们发现:

第一个月有一对兔子

第二个月有一对兔子

第三个月有两对兔子

第四个月有三对兔子

第五个月有五对兔子

……

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

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

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

递归方法如下:

#include <iostream>

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(i)<<endl;

}

但是我们仔细思考,这个递归的方法貌似有些计算是重复计算了,比如说当计算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 <iostream>
using namespace std;
const int N=13;
int dp[N];

int main() {
	int n;
	dp[1]=1;
	dp[2]=1;
	cout<<dp[1]<<endl<<dp[2]<<endl;
	for(int i=3;i<13;++i)
	{
		dp[i]=dp[i-1]+dp[i-2];
		cout<<dp[i]<<endl;
	}
	return 0;
}

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

我们可以思考一下

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

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

整个代码如下所示:

#include <iostream>
using namespace std;
int main() {

	int first=1,second=1,third;
	cout<<first<<endl<<second<<endl;
	for(int i=3;i<13;++i)
	{
		third=first+second;
		cout<<third<<endl;
		first=second;
		second=third;
	}
	return 0;
}

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

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值