题目一:
假设有排成一行的N个位置,记为1~N,N一定大于或等于2开始时机器人在其中的M位置上(M一定是1~N中的一个)
如果机器人来到1位置,那么下一步只能往右来到2位置;
如果机器人来到N位置,那么下一步只能往左来到N-1位置;
如果机器人来到中间位置,那么下一步可以往左走或者往右走;
规定机器人必须走K步,最终能来到P位置(P也是1~N中的一个)的方法有多少种给定四个参数N、M、K、P,返回方法数。
暴力递归解法
定性分析:在递归的推进过程中,机器人的剩余可走步数在不断减少(每次-1),当剩余步数为0时,通过判断当前位置是否与终点位置重合,进而返回成功或者失败。
//start:当前位置 N:N个位置 aim:目的位置 K:还需要走K步
//返回一个整数记录有多少条不同路径
public static int RobotWalk1(int start, int N, int aim, int K) {
//剩余步数为0;若成功抵达终点,记录一条不同的成功路径。
if ( K == 0 ) {
return start == aim ? 1 : 0;
}
//当抵达最左边时,此时只能向右走;
if (start == 1) {
return RobotWalk1(2,N,aim,K-1);
}
//当抵达最右边时,此时只能向左走
if (start == N) {
return RobotWalk1(N-1,N,aim,K-1);
}
//处于中间位置,既可向左也可向右;
return RobotWalk1(start-1,N,aim,K-1)
+ RobotWalk1(start+1,N,aim,K-1);
}
不难看出,(start,K)是递归过程中的不断变化的量;在递归的过程中,部分(statr,K)出现了重复运算,为了消除这种重复运算,我们可以对上述算法进行优化,由此引入动态规划解法。
动态规划解法
我们可以采用一张表来记录每次运算过的(start,K)所对应的值。这样在第二次乃至之后再次遇见该值,可直接通过查表返回,从而减少了大幅的运算量。
这里我们采用二维数组记录start/K两个变量。
我们用一个具体的例子来说明:
int m = RobotWalk3(2,5,4,6);
当前位置start == 2;共有N == 5 个位置; 终点aim == 4; 走K==6步;问共有多少种走法。
以下四点依次对应上述暴力递归算法的四步判断:
-
当K==0时,当且仅当start==aim时,机器人才能抵达终点。(本题aim==4)故第一列的值如下表所示。注:start==0的情况不存在。
-
当start==1时,机器人只能向右走,故机器人从start==1处走K步相当于机器人从start==2处走K-1步。所以当start==1时,(1,K)处的值与(2,K-1)处的值相等。
-
同理,当start==N时,(N,K)处的值与(N-1,K-1)处的值也相等。
-
当start位于中间时:机器人既可以向左走也可以向右走,值为向左向右的和;故
(start,K)=(start-1,K-1)+(start+1,K-1)
start\K | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 0 | 0 | 1 | 0 | 4 | 0 |
2 | 0 | 0 | 1 | 0 | 4 | 0 | 13 |
3 | 0 | 1 | 0 | 3 | 0 | 9 | 0 |
4 | 1 | 0 | 2 | 0 | 5 | 0 | 14 |
5 | 0 | 1 | 0 | 2 | 0 | 5 | 0 |
因此我们只需要写出这张表,即可直接返回(start==2,K==6)所对应的值。(本题答案为13)
算法如下:
public static int RobotWalk3(int start, int N, int aim, int K) {
int[][] dp = new int[N+1][K+1];
//得到第一列(start,0)的值
dp[aim][0] = 1;
//一列一列的遍历添值
for (int j = 1; j <= K; j++) {
//得到每一列start==1处的值
dp[1][j] = dp[2][j-1];
for (int i = 2; i < N; i++) {
得到每一列机器人处于中间处的值
dp[i][j] = dp[i+1][j-1] + dp[i-1][j-1];
}
//得到每一列start==N处的值
dp[N][j] = dp[N-1][j-1];
}
return dp[start][K];
}
代码运行:
public static void main(String[] args) {
int m = RobotWalk3(2,5,4,6);
System.out.println(m);
}
//输出如下:
13