题目描述
假设把整个棋盘放入第一象限,棋盘的最左下角是(0,0)位置,整个棋盘就是横坐标上10条线,纵坐标上9条线的区域,给出三个参数x,y,k;
返回“马”从(0,0)位置出发,必须走K步,最终落在(x,y)上的方法数有多少种?
比如:K = 10;x = 7;y = 7 ,返回多少种方法?
题目解析
暴力递归
马在一个位置可以有八种选择
当小马在一个位置时:
- 第一步:判断当前位置是否合法,如果不合法,返回0(表示不可能到达目的地)
- 第二步:如果当前没有步数了,此时判断是否已经到达了指定位置,如果是,那么返回1
- 第四步:否则,有8个方向可以选择,将这些方法全部加起来就是最终答案
代码如下:
class Solution {
// 当前来到的位置是(x,y)还剩下rest步需要跳
// 跳完rest步,正好跳到a,b的方法数是多少?
// 横坐标上10条线,纵坐标上9条线的区域
int process(int x, int y, int rest, int a, int b) {
if (x < 0 || x > 9 || y < 0 || y > 8) {
return 0;
}
if (rest == 0) {
return (x == a && y == b) ? 1 : 0;
}
int ways = process(x + 2, y + 1, rest - 1, a, b);
ways += process(x + 1, y + 2, rest - 1, a, b);
ways += process(x - 1, y + 2, rest - 1, a, b);
ways += process(x - 2, y + 1, rest - 1, a, b);
ways += process(x - 2, y - 1, rest - 1, a, b);
ways += process(x - 1, y - 2, rest - 1, a, b);
ways += process(x + 1, y - 2, rest - 1, a, b);
ways += process(x + 2, y - 1, rest - 1, a, b);
return ways;
}
public :
int jump(int a, int b, int k) {
return process(0, 0, k, a, b);
}
};
测试:
std::cout << a.jump(2, 3, 3); //4
暴力递归改备忘录
我们来分析下递归函数的可变参数与变化范围
int process(int x, int y, int rest, int a, int b)
这里的可变参数有x、y、rest,一共有三个,说明要准备一个三维数组
横坐标上9条线,纵坐标上10条线的区域,因此
c
a
c
h
e
[
9
]
[
10
]
[
K
+
1
]
cache[9][10][K + 1]
cache[9][10][K+1]
class Solution {
// 当前来到的位置是(x,y)还剩下rest步需要跳
// 跳完rest步,正好跳到a,b的方法数是多少?
int process(int x, int y, int rest, int a, int b, std::vector<std::vector<std::vector<int>>> cache) {
if (x < 0 || x > 9 || y < 0 || y > 8){
return 0;
}
if(cache[x][y][rest] != -1){
return cache[x][y][rest];
}
if (rest == 0) {
return (x == a && y == b) ? 1 : 0;
}
int ways = process(x + 2, y + 1, rest - 1, a, b, cache);
ways += process(x + 1, y + 2, rest - 1, a, b, cache);
ways += process(x - 1, y + 2, rest - 1, a, b, cache);
ways += process(x - 2, y + 1, rest - 1, a, b, cache);
ways += process(x - 2, y - 1, rest - 1, a, b, cache);
ways += process(x - 1, y - 2, rest - 1, a, b, cache);
ways += process(x + 1, y - 2, rest - 1, a, b, cache);
ways += process(x + 2, y - 1, rest - 1, a, b, cache);
cache[x][y][rest] = ways;
return cache[x][y][rest];
}
public :
int jump(int a, int b, int k) {
std::vector<std::vector<std::vector<int>>> cache(10,
std::vector<std::vector<int>>(9,
std::vector<int>(k + 1, -1)));
return process(0, 0, k, a, b, cache);
}
};
int main(){
// printf("%d\r\n", Fab(4));
Solution a;
vector<int> nums {1, 2, 3, 1};
std::cout << a.jump(2, 3, 7);
}
暴力递归改动态规划
(1)准备一张表,递归函数的可变参数与变化范围
int process(int x, int y, int rest, int a, int b)
这里的可变参数有x、y、rest,一共有三个,说明要准备一个三维数组
- x取值范围为:0~10
- y取值范围为:0~9
- rest取值范围为:0~k
所以,准备数组如下:
std::vector<std::vector<std::vector<int>>> dp(10,
std::vector<std::vector<int>>(9,
std::vector<int>(k + 1, 0)));
(2)返回值。分析主函数是如何调用递归函数的
return process(0, 0, k, a, b, cache);
所以应该返回:dp[0][0][k]
(3)填表
(3.1)第一步,应该确保参数合法
if (x < 0 || x > 9 || y < 0 || y > 8){
return 0;
}
(3.2)第二步,使用base case来初始化这张表
if (rest == 0) {
return (x == a && y == b) ? 1 : 0;
}
- 此时k为0,也就是初始化最底层的那个平面的某个点,也就是
dp[a][b][0] = 1
(3.3)分析普通情况。由
int ways = process(x + 2, y + 1, rest - 1, a, b, cache);
ways += process(x + 1, y + 2, rest - 1, a, b, cache);
ways += process(x - 1, y + 2, rest - 1, a, b, cache);
ways += process(x - 2, y + 1, rest - 1, a, b, cache);
ways += process(x - 2, y - 1, rest - 1, a, b, cache);
ways += process(x - 1, y - 2, rest - 1, a, b, cache);
ways += process(x + 1, y - 2, rest - 1, a, b, cache);
ways += process(x + 2, y - 1, rest - 1, a, b, cache);
上一层的dp[x][y][k]依赖下一层平面的八个方向dp[?][?][k-1],当然我们必须确保[?][?]合法
所以,先从k调度,再填写xy
class Solution {
int pick(std::vector<std::vector<std::vector<int>>> &dp, int x, int y, int rest) {
if (x < 0 || x > 9 || y < 0 || y > 8){
return 0;
}
return dp[x][y][rest];
}
public :
int jump(int a, int b, int k) {
std::vector<std::vector<std::vector<int>>> dp(10,
std::vector<std::vector<int>>(9,
std::vector<int>(k + 1, 0)));
dp[a][b][0] = 1;
for (int rest = 1; rest <= k; ++rest) {
for (int x = 0; x < 10; ++x) {
for (int y = 0; y < 9; ++y) {
int ways = pick(dp, x + 2, y + 1, rest - 1);
ways += pick(dp, x + 1, y + 2, rest - 1);
ways += pick(dp, x - 1, y + 2, rest - 1);
ways += pick(dp, x - 2, y + 1, rest - 1);
ways += pick(dp, x - 2, y - 1, rest - 1);
ways += pick(dp, x - 1, y - 2, rest - 1);
ways += pick(dp, x + 1, y - 2, rest - 1);
ways += pick(dp, x + 2, y - 1, rest - 1);
dp[x][y][rest] = ways;
}
}
}
return dp[0][0][k];
}
};