动态规划--兔子繁殖问题

17 篇文章 3 订阅

假设一对新生兔子,从第三个月开始,每个月都会下t=1对新生兔子,新生的兔子具有同样规律,
而且假定兔子不死亡,现在从第一个月1对兔子开始,求第n个月最末一天兔子有多少对? 假如t=2,求出新的数列结果。
假如兔子只活6个月(即兔子从它出生的第1个月算起,在第6个月的最末一天会死亡),求对应结果。

(1)
设第n个月兔子总对数和新出生兔子对数分别为f(n),g(n), 则有
f(n)=∑g(k) , 1<=k<=n

            1       n=1       可等价为           0        n<1
    g(n)={  0       n=2      =========>  g(n)={  1        n=1 
            f(n-2)  n>2                          f(n-2)   n>1 		可进一步数学变化得到 		f(n)=∑g(k)【1<=k<=n】
      =g(n)+∑g(k)【1<=k<=n-1】 			=f(n-2)+∑g(k)【1<=k<=n-1】 			=f(n-2)+f(n-1) 		且f(1)=f(2)=1; 		此式即为经典的斐波那契数列公式。   

(2)同理

    f(n)=∑g(k) , 1<=k<=n

            1       n=1       可等价为           0        n<1
    g(n)={  0       n=2      =========>  g(n)={  1        n=1 
           2f(n-2)  n>2                         2f(n-2)   n>1
    数学变化可得到f(n)=2f(n-2)+f(n-1),且f(1)=f(2)=1;   

(3)同理分析可得

    f(n)=∑g(k),n-4<=k<=n  //即第n个月兔子总数=近5个月新生兔子总数

            0                            n<1
    g(n)={  1                            n=1 
            g(n-5)+g(n-4)+g(n-3)+g(n-2)  n>1 		//g(n)常规写法如下 
          0                            n<1 //可以省 
            1                            n=1
    g(n)={  0                            n=2
            1                            n=3
            1                            n=4
            2                            n=5
            g(n-5)+g(n-4)+g(n-3)+g(n-2)  n>5    以下为(3)的三种dp写法。

在这里插入图片描述

#include <iostream>
#include <stdio.h>
#include <conio.h>
#define MAX 1000
using namespace std;

int F1[MAX],G1[MAX];//对应f(n),g(n)的存储
int g1(int n)//根据g(n)等价的递推关系式而写
{
	if(n<1)   return 0;//异常
	if(G1[n]) return G1[n];//如果G[n]已经计算过,直接返回结果
	if(n==1)  return G1[n]=1;//保存结果并返回
	else      return G1[n]=g1(n-5)+g1(n-4)+g1(n-3)+g1(n-2);
}

int f1(int n)//求f(n)
{
	int k;
	if(n<1)   return 0;//异常
	if(F1[n]) return F1[n];//如果F[n]已经计算过,直接返回结果
	for(k=n-4;k<=n;k++) 
		F1[n] += g1(k); //对应f(n)=∑g(k) ,n-4<=k<=n
	return F1[n];
}

int G2[MAX]={0};//
int g2(int n)//根据g(n)等价的递推关系式而写
{
	int i;
	if(n<1)   return 0;//异常
	for(i=1;i<=n;i++)
		if(i==1)  G2[i]=1;
	    else      
		{
			for(int j=i-5;j<=i-2;j++)
			  if(j>0) G2[i] +=G2[j];//g[i]=g[i-5]+g[i-4]+g[i-3]+g[i-2]
              //i,j都表示月份,i值可能为2,3,4,5,里面有j<1情况 
		}
	return G2[n]; 
	/* 按g(n)常规写法的代码如下
	for(i=1;i<=n;i++)
		if(i==1)  G2[i]=1;
		else if(i==2) G2[i]=0; 
		else if(i==3) G2[i]=1;
		else if(i==4) G2[i]=1;
		else if(i==5) G2[i]=2;
	    else      
		{//处理i>=6 
		    G2[i]=G2[i-5]+G2[i-4]+G2[i-3]+G2[i-2];
			//for(int j=i-5;j<=i-2;j++) 
			   //if(j>0) G2[i] +=G2[j];//i>=6时,if(j>0)可以省 
		}
	*/
}

int F2[MAX]={0};
int f2(int n)//求f(n)
{
	int i; 
	if(n<1)  return 0;//异常
	for(i=n-4;i<=n;i++) 
		F2[n] += G2[i]; //对应f(n)=∑g(k) ,n-4<=k<=n
      //此处G2[i]也可以改为调用了g2(i),因为g2算的结果已经放在全局G2[]中,所以可以直接引用已经算好的值 
	return F2[n];
}

//G3[6]为f3,g3共享,存放近6个月新生兔子对数
int G3[6];//压缩存储写法:只用存储g(n-5)~g(n)这6个值
int f3(int n)
{
	int i,s=0;
	if(n<1)  return 0;//异常
	if(n>5)  n=5; //如果n>5,直接将G3[0]~G3[4]累加返回即可
	for(i=0;i<n;i++) 
		s += G3[i]; //对应f(n)=∑g(k) ,n-4<=k<=n
	return s;
}
int g3(int n)
{
	int i,j;
	memset(G3,0,6*sizeof(int));
	G3[0]=1;
	G3[1]=0;
	G3[2]=1;
	G3[3]=1;
	G3[4]=2; //初始化前5个月新生兔子数,类似于斐波那契数列初始时f1=1;f2=1;
	for(i=6;i<=n;i++)//求第i个月新生兔子数,存放在G3[5]中
	{
		G3[5]=G3[0]+G3[1]+G3[2]+G3[3];
		for(j=0;j<5;j++) G3[j]=G3[j+1];//覆盖已经无用的G3[0],即第i个月~第i-4个月的新生兔子数依次存放在G3[4]~G3[0]中
	}
	//当for结束后,则第n个月~第n-4个月的新生兔子数依次存放在G3[4]~G3[0]中
	if(n<5) return G3[n-1];
	else    return G3[4];
}

int main()
{
	int n,i;
	cout<<"input n = ";
	cin>>n;
	cout<<"       新兔子数     总兔子数 \n";
	g2(n);
	for (i = 1; i <= n; i++) 
	{
		printf("\nn=%2d %10d %10d",i,G2[i],f2(i));
	}
	getche();
	return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值