一个顽猴在一座有k级台阶的山上爬山跳跃,猴子上山一步可跳1级或跳3级,试求顽猴上山的N级台阶有多少种不同的跳法;
1.递推设计:
这一问题实际上是一个整数有序可重复拆分问题;
试设置数组应用递推求解,设上k级台阶的不同跳法为f(k)种;
(1)、探求f(k)的递推关系;
假设:k=100;
上山最后一步到达第100级台阶,完成上山,共有f(100)种不同的爬法,到第100级之前位于哪一级呢?无非是位于第99级(上跳1级即到),有f(99)种;
或位于第97级(上跳3级即到),有f(97)种,于是:
- f(100)=f(99)+f(97)
- f(99)= f(98)+f(96)
- f(97)= f(96)+f(94)
- 依次类推
以此类推,一般地有递推关系:
- f(k)=f(k-1)+f(k-3) (k>3)
(2)、确定初始条件:
-
f(1)=1,即1=1;
-
f(2)=1,即2=1+1(注意:跳法中不允许直接跳2级);
-
f(3)=2,即3=1+1+1,3=3;
(3)、实施递推;
根据以上递推关系与初始条件设置一重k(4~n)循环,循环外确定初始条件,循环内实施递推:
- f[k]=f[k-1]+f[k-3],即可求出f(n);//与斐波那契数列的求解一样
此具体案例的递推设计比较简单,时间复杂度为O(n);
程序设计(Java):
public static void main(String[] args){ Scanner sc = new Scanner(System.in); int k = sc.nextInt(); System.out.println(recursion(k)); } static int recursion(int k){ //f(1)=1,即1=1; //f(2)=1,即2=1+1(注意:跳法中不允许直接跳2级); //f(3)=2,即3=1+1+1,3=3; if(k==1 || k==2){ return 1; } if(k==3){ return 2; } return recursion(k-1)+recursion(k-3); }
2.一般情形的分级递推:
把问题引申为爬山n级台阶,一步有m种跨法,具体一种跨法跳多少级均从键盘输入;
(1)、分级递推设计;
1)、设置两个数组;
爬山t级台阶的不同爬法为f(t),从键盘输入一步跨多少级的m个整数为x[i](i=1,2,……,m);
这里的整数x(1),x(2),……,x(m)(约定x(1)< x(2)<……< x(m)< n)为键盘输入,事前并不知道,因此不能在设计时简单地确定初始值f(x(1)),f(x(2)),……;
事实上,可以把初始条件放在分级递推中求取,应用多关系分级递推算法完成递推;
2)、确定f(t)的递推关系;
当t< x(1)时,f(t)=0,f(x(1))=1 (初始条件);
当x(1)< t<=x(2)时,第1级递推:f(t)=f(t-x(1));
当x(2)< t<=x(3)时,第2级递推:f(t)=f(t-x(1))+f(t-x(2));
===================================
一般的,当x(k)< t<=x(k-1),k=1,2,……,m-1,有第k级递推:
f(t)=f(t-x(1))+f(t-x(2))+……f(t-x(k));
当x(m)< t时,第m级递推:
f(t)=f(t-x(1))+f(t-x(2))+……+f(t-x(m));
当t=x(2),或t=x(3),……,或t=x(m)时,按上面递推求f(t)外,还要加上1,道理很简单,因为此时t本身即为一个一步到位的爬法,为此,应在以上递推基础上添加:
f(t)=f(t)+1 (t=x(2),x(3),……,x(m));
======================
所求的目标为:
f(n)=f(n-x(1))+f(n-x(2))+……+f(n-x(m));
==========================================
这一递推式是我们设计的依据;
3)、设x(m+1)的技巧;
在递推设计中可以把台阶数n记为数组元素x(m+1),这样处理是巧妙地,可以按相同的递推规律递推计算,简化算法设计,最后一项f(x(m+1))即为所求f(n);
最后输出f(n)即f(x(m+1))时必须把额外所添加的1减去;
注意:上述求解过程中存在重复计算某个值,可以画出递归树来看,可以使用备忘录或者table表去优化。
可参考:https://www.cnblogs.com/controller666/p/14520459.html 带备忘录的递归解法
优化代码:
public static void main(String[] args) { Scanner sc = new Scanner(System.in); int k = sc.nextInt(); System.out.println(recursion(k)); } public static int recursion(int n) { int[] res = new int[n+1]; //备忘录全初始化为-1 for (int i = 0; i < n+1; i++) { res[i] = -1; } //等于0的情况返回就是0 if (n == 0) { res[n] = 0; return 0; } //等于1或者2的情况就是1 并记录已计算过 if (n == 1) { res[n] = 1; return 1; } if (n == 2) { res[2] = 1; return 1; } //等于3的情况就是2 并记录已计算过 if (n == 3) { res[3] = 2; return 2; } //如果数组中的值不是-1,则说明计算过,则直接返回,不再重复计算 if (res[n] != -1) return res[n]; res[n] = recursion(n - 1) + recursion(n - 3); return res[n]; }