最近复习到了递推,私以为和递归有异曲同工之妙,都是找规律嘛~~~
首先说说递推。
递推就是每一项都和他前面的若干项有一定的关联,这种关联一般可以通过递推关系式来表示,通过前面的若干项来得出某一项的数据。其实理解递推的关键就是本项和前一项或者前两项存在着某种关系。
下面来一道经典的递推例题,马踏过河卒。
A点有一个过河卒,需要走到目标B点。卒行走规则:可以向下、或者向右。同时在棋盘上的任一点有一个对方的马(如上图的C点),该马所在的点和所有跳跃一步可达的点称为对方马的控制点。例如上图C点上的马可以控制9个点(图中的P1,P2⋯P8和C)。卒不能通过对方马的控制点。
棋盘用坐标表示,A点(0,0)、B点(n,m)、C 点(cx,cy)(0<cx<n≤20,0<cy<m≤20)。现在要求你计算出过河卒从A 点能够到达B 点的路径的条数。注:象棋中马走“日”。
输入格式
输入4个整数,n,m,cx,cy,分别表示B 点的横纵坐标和C 点的横纵坐标。
输出格式
输出一个整数,代表从A 点走到B点的所有路径数。
样例输入
5 5 2 4
样例输出
14
案例分析:因为前面学到了广度优先搜索,可能有的小伙伴先用是它,但是它带来的弊端是时间会慢,会超时。所以我们想到了使用递推的方法。
根据题目的要求,卒只能向下或者向右走,那么想要到达棋盘上的一个点,有两种方式:从左边的格子过来,或者从上边的格子过来。所以,过河卒到达某点的路径数目等于到达与其相邻的左边点和上边点的路径数目和。我们用 Fi,j来表示到达点 (i,j) 的路径数目。所以递推式为:Fi,j=Fi−1,j+Fi,j−1。我们根据递推式发现,我们可以用逐行或逐列的递推方法求出从起点到终点的路径数目。我们来想一下边界条件,因为(0,0)是卒的起始位置,那么 F0,0=1。我们在不考虑马控制点的情况下,可以写出递推代码。
具体代码如下所示:
import java.util.Scanner;
public class ex47 {
static int[][] map=new int[31][31];//马的控制点
static long[][] f=new long[31][31];//计数
static int n,m,cx,cy;//坐标
static int[][] x={{1,2},{-1,2},{1,-2},{-1,-2},{2,-1},{2,1},{-2,1},{-2,-1}};
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner in=new Scanner(System.in);
n=in.nextInt();
m=in.nextInt();
cx=in.nextInt();
cy=in.nextInt();
for(int i=0;i<30;i++){
for(int j=0;j<30;j++)
map[i][j]=0;//将数组初始化为0
}
map[cx][cy]=1;//马控制点
for(int i=0;i<8;i++){
int tx=cx+x[i][0];//计算马控制点横坐标
int ty=cy+x[i][1];//计算马控制点纵坐标
if(tx>=0&&tx<=n&&ty>=0&&ty<=m)
map[tx][ty]=1;
}
f[0][0]=1;
for(int i=0;i<=n;i++){
for(int j=0;j<=m;j++){
if(i!=0&&map[i][j]!=1)
f[i][j]=f[i][j]+f[i-1][j];
if(j!=0&&map[i][j]!=1)
f[i][j]=f[i][j]+f[i][j-1];
}
}
System.out.println(f[n][m]);
}
}
然后我们来说说递归~
递归,简明扼要,就是自己用自己。这里需要注意的一点就是,如果递归过程无法到达递归边界或者递归次数过多,则会造成栈的溢出。
递归算法一般用于解决三类问题:
1.函数的定义是递归的,如阶乘n!或者Fibonacci函数
2.数据的结构形式按递归定义,如树的遍历,图的深度优先搜索
3.问题的解法是递归的,如回溯法--->应用典范
来道递归例题
习题:汉诺塔
汉诺塔(又称河内塔)问题是源于印度一个古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。
现在蒜头君开始玩汉诺塔游戏, 他放了n片黄金圆盘在第一根柱子上,从上到下依次编号为 1− n, 1号圆盘最小,n号圆盘最大。蒜头君移动第i号圆盘的时候需要花费i点体力。现在蒜头君想把圆盘全部移动到底2根柱子上,移动过程中蒜头君必须准守游戏规则。
现在蒜头君想知道他完成游戏的最小移动次数和最少消耗的体力。
输入格式
输入一个正整数 n(1≤n≤60)表示黄金圆盘的个数
输出格式
一行输出2个数,表示最小移动次数和最小消耗的体力,中间用一个空格隔开。
样例输入1
2
样例输出1
3 4
样例输入2
3
样例输出2
7 11
import java.util.Scanner;
public class ex33 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner in=new Scanner(System.in);
int n=in.nextInt();
Hannuotan hn=new Hannuotan();
long time=hn.CalculateTime(n);
long energy=hn.CalculateEnergy(n);
System.out.print(time+" ");
System.out.println(energy);
}
}
class Hannuotan{
public long CalculateTime(int n){//移动次数
if(n==1)
return 1;
return 2*(CalculateTime(n-1))+1;//将n-1个从A->C,第n个从A->B,最后再将n-1个从C->B
}
public long CalculateEnergy(int n){//消耗体能
if(n==1)
return 1;
return 2*(CalculateEnergy(n-1))+n;
}
}
酱~