假设一对新生兔子,从第三个月开始,每个月都会下t=1对新生兔子,新生的兔子具有同样规律,
而且假定兔子不死亡,现在从第一个月1对兔子开始,求第n个月最末一天兔子有多少对? 假如t=2,求出新的数列结果。
假如兔子只活6个月(即兔子从它出生的第1个月算起,在第6个月的最末一天会死亡),求对应结果。
(1)
设第n个月兔子总对数和新出生兔子对数分别为f(n),g(n), 则有
f(n)=∑g(k) , 1<=k<=n1 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;
}