首页 > 试题广场 >

象棋中马的跳法

[编程题]象棋中马的跳法
  • 热度指数:819 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
请同学们自行搜索或者想象一个象棋的棋盘,然后把整个棋盘放入第一象限,棋盘的最左下 角是(0,0)位置。那么整个棋盘就是横坐标上9条线、纵坐标上10条线的一个区域。给你三个参数,x,y,k,返回如果“马”从(0,0)位置出发,必须走k步,最后落在(x,y)上的方法数 有多少种?

输入描述:
输入一行数字,包含3个参数x,y,k


输出描述:
输出一个数,代表有几种
示例1

输入

7 7 10

输出

515813

象棋问题

https://www.nowcoder.com/questionTerminal/c45704a41617402fb5c34a1778bb2645

把象棋棋盘放入第一象限,棋盘的最左下角是(0, 0)位置,那么整个棋盘就是横坐标上9条线,纵坐标上10条线的区域给你三个参数x、y、k,返回“马”从(0, 0)位置出发,必须走k步,最后落在(a,b)上的方法有多少种

暴力递归

象棋中的规则:马只能日字跳

所以下面的跳法是错的:

image-20220714165421939


正确的跳法:

从(x,y)位置跳,可以往八个位置跳,有八种跳法

p1:(x+2,y+1) p2:(x+1,y+2) p3:(x-1,y+2) p4:(x-2,y+1)

p5:(x-2,y-1) p6:(x-1,y-2) p7:(x+1,y-2) p8:(x+2,y-1)

image-20220714175725132


递归全展开,一个位置8种可能性,一共跳k步,所以暴力方法时间复杂度:O(8^k)

这八个位置分别再往后跳,最后到达(a,b)的方法数之和就是从(x,y)出发到达(a,b)的方法数


暴力方法在OJ超时!

/*
process函数的含义:
当前来到的位置是(x,y),还剩下rest步需要跳,跳完rest步之后正好跳到(a,b)的方法数是多少
*/
int process1(int x, int y, int rest, int a, int b)\
{
    //防止越界: 棋盘是10*9   
    //范围:x:0~9  y:0~8
    if (x < 0 || x > 9 || y < 0 || y > 8) {
        return 0;
    }
    //base case:剩余步数为0,如果刚好跳到了(a,b)位置就是一种方法,否则不是
    if (rest == 0) 
    {
        return (x == a && y == b) ? 1 : 0;
    }
    //从(x,y)分别往八个方向跳,剩rest-1步走, 要跳到(a,b) 的方法数 
    int ways = 0;
    ways += process1(x + 2, y + 1, rest - 1, a, b);
    ways += process1(x + 1, y + 2, rest - 1, a, b);
    ways += process1(x - 1, y + 2, rest - 1, a, b);
    ways += process1(x - 2, y + 1, rest - 1, a, b);
    ways += process1(x - 2, y - 1, rest - 1, a, b);
    ways += process1(x - 1, y - 2, rest - 1, a, b);
    ways += process1(x + 1, y - 2, rest - 1, a, b);
    ways += process1(x + 2, y - 1, rest - 1, a, b);
    return ways;
}
int jump1(int a, int b, int k)
{
    //返回从(0,0)位置开始跳,跳k步,最后跳到(a,b)的方法数
    return process1(0, 0, k,a,b);
}

int main()
{
    cout << jump1(7, 7, 10) << endl;//515813
    return 0;
}

记忆化搜索

a,b是固定参数,每次调用都不会变,而x,y,k每次调用过程都会变,因此process函数的调用结果取决于 x y k的值,我们只要用一个三维表来记录每个调用过程的返回值便能大大提高

x范围:09 y范围:08 rest的范围:0~k 所以开辟10*9*(k+1)的三维表

vector<vector<vector<int>>> dp(10, vector<vector<int>>(9, vector<int>(k + 1,-1)));

最初所以元素初始化为-1,表示每个位置都没有算过

image-20220714200351971


记忆化搜索在这里OJ能过!

/*
process函数的含义:
当前来到的位置是(x,y),还剩下rest步需要跳,跳完rest步之后正好跳到(a,b)的方法数是多少
*/
int process2(int x, int y, int rest, int a, int b, vector<vector<vector<int>>>& dp)
{
    //防止越界: 棋盘是10*9   
    //范围:x:0~9  y:0~8
    if (x < 0 || x > 9 || y < 0 || y > 8) {
        return 0;
    }
    if (dp[x][y][rest] != -1)//如果值不为-1.说明之前算过这个过程,直接在表中拿值
    {
        return dp[x][y][rest];
    }
    //base case:剩余步数为0,如果刚好跳到了(a,b)位置就是一种方法,否则不是
    if (rest == 0)
    {
        int ans =  (x == a && y == b) ? 1 : 0;
        dp[x][y][rest] = ans;
        return ans;
    }
    //从(x,y)分别往八个方向跳,剩rest-1步走, 要跳到(a,b) 的方法数 
    int ways = 0;
    ways += process2(x + 2, y + 1, rest - 1, a, b, dp);
    ways += process2(x + 1, y + 2, rest - 1, a, b, dp);
    ways += process2(x - 1, y + 2, rest - 1, a, b, dp);
    ways += process2(x - 2, y + 1, rest - 1, a, b, dp);
    ways += process2(x - 2, y - 1, rest - 1, a, b, dp);
    ways += process2(x - 1, y - 2, rest - 1, a, b, dp);
    ways += process2(x + 1, y - 2, rest - 1, a, b, dp);
    ways += process2(x + 2, y - 1, rest - 1, a, b, dp);
    dp[x][y][rest] = ways;
    return ways;
}
//x范围:0~9 y范围:0~8 rest的范围:0~k
//所以开辟10*9*(k+1)的三维表
int jump2(int a, int b, int k)
{
    //返回从(0,0)位置开始跳,跳k步,最后跳到(a,b)的方法数
    vector<vector<vector<int>>> dp(10, vector<vector<int>>(9, vector<int>(k + 1,-1)));
    process2(0, 0, k, a, b,dp);
    //dp[i][j][k]的含义:从(i,j)位置,跳k步跳到目标位置(a,b)的方法数
    return dp[0][0][k];
}

严格位置依赖的动态规划

使用dp表的时间复杂度:O(10*9*K ) ->时间复杂度:O(K)

任何的rest层的东西 只依赖rest-1层,所以我们先把第0层填好,再填第一层.... 从上往下填

注意:可能存在下标越界的情况,所以封装一个函数在dp表中拿值


//为了防止下标越界,封装一个函数检测, 不越界才返回表中的值
int pick(vector<vector<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];
}
int jump3(int a, int b, int k)
{
    vector<vector<vector<int>>> dp(10, vector<vector<int>>(9, vector<int>(k + 1)));
    //rest=0时(第0层),只有x=a,y=b位置是1 剩下第0层的位置是0
    dp[a][b][0] = 1;
    //从第一层开始填,填到第k层
    for (int rest = 1; rest <= k; rest++) 
    {
        for (int x = 0; x < 10; x++)
        {
            for (int y = 0; y < 9; y++)
            {
                int ways = 0;
                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;
            }
        }
    }
    //dp[i][j][k]的含义:从(i,j)位置,跳k步跳到目标位置(a,b)的方法数
    //题目要求是从(0,0)出发,目标是跳k步到(a,b)
    return dp[0][0][k];
}
发表于 2022-07-14 20:09:52 回复(1)
import java.util.*;

public class Main {
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int x = sc.nextInt();
        int y = sc.nextInt();
        int nums = sc.nextInt();
        int[][][] dp = new int[9][10][nums+1];
        dp[0][0][0] = 1;
        for(int i=1; i<=nums; i++){
            for(int j=0; j<9; j++){
                for(int k=0; k<10; k++){
                    dp[j][k][i] += getValue(i-1,j-1,k+2,dp);
                    dp[j][k][i] += getValue(i-1,j-2,k+1,dp);
                    dp[j][k][i] += getValue(i-1,j-2,k-1,dp);
                    dp[j][k][i] += getValue(i-1,j-1,k-2,dp);
                    dp[j][k][i] += getValue(i-1,j+1,k-2,dp);
                    dp[j][k][i] += getValue(i-1,j+2,k-1,dp);
                    dp[j][k][i] += getValue(i-1,j+2,k+1,dp);
                    dp[j][k][i] += getValue(i-1,j+1,k+2,dp);
                }
            }
        }
        System.out.println(dp[x][y][nums]);
    }
    
    public static int getValue(int l,int x,int y,int[][][] dp){
        if(x<0 || x>=9 || y<0 || y>=10){
            return 0;
        }
        return dp[x][y][l];
    }
}

发表于 2022-02-03 12:03:54 回复(0)
递归和动态规划两种解法
递归解法容易理解,动态规划效率更高
import java.util.*;

public class Main{
    
    public static void main(String[] args){
        Scanner scanner = new Scanner(System.in);
        int x = scanner.nextInt();
        int y = scanner.nextInt();
        int k = scanner.nextInt();
        
        System.out.print(waysdp(k, x, y) + "");
    }
    
    // 动态规划
    public static int waysdp(int k, int x, int y){
        int[][][] dp = new int[9][10][k + 1];
        // 达成条件
        dp[x][y][0] = 1;
        for(int rest = 1; rest < k + 1; rest++){
            for(int i = 0; i < 9; i++){
                for(int j = 0; j < 10; j++){
                    dp[i][j][rest] = (getValue(dp, i - 1, j - 2, rest - 1) + 
                        getValue(dp, i - 2, j - 1, rest - 1) + 
                        getValue(dp, i - 1, j + 2, rest - 1) + 
                        getValue(dp, i - 2, j + 1, rest - 1) + 
                        getValue(dp, i + 1, j - 2, rest - 1) + 
                        getValue(dp, i + 2, j - 1, rest - 1) + 
                        getValue(dp, i + 1, j + 2, rest - 1) + 
                        getValue(dp, i + 2, j + 1, rest - 1));
                }
            }
        }
        return dp[0][0][k];
    }
    
    public static int getValue(int[][][] dp, int a, int b, int rest){
        if(a > 8 || a < 0 || b > 9 || b < 0) {
            return 0;
        }
        return dp[a][b][rest];
    }
    
    public static int recursion(int a , int b, int rest, int x, int y){
        if(a > 8 || a < 0 || b > 9 || b < 0) {
            return 0;
        }
        if(rest == 0){
            return  a == x && b == y ? 1 : 0;
        }

        return recursion(a - 1, b - 2, rest -1, x, y) + 
            recursion(a - 2, b - 1, rest -1, x, y) + 
            recursion(a - 1, b + 2, rest -1, x, y) + 
            recursion(a - 2, b + 1, rest -1, x, y) + 
            recursion(a + 1, b - 2, rest -1, x, y) + 
            recursion(a + 2, b - 1, rest -1, x, y) + 
            recursion(a + 1, b + 2, rest -1, x, y) + 
            recursion(a + 2, b - 1, rest -1, x, y);
    }
    
}

编辑于 2021-04-28 17:22:23 回复(0)