象棋跳马问题(由暴力递归->动态规划)

跳马问题解释:

        象棋中马走日,从(0,0)点出发到达目标位置,规定走K步,问:能走到目标位置的所有方法数,象棋棋盘规模10*9大小。

    public static int jump(int a, int b, int K) {
        return process(0, 0, K, a, b);
    }

方法中的参数解释:

        1.int a和 int b,代表目标位置是(a,b)

        2.int K,代表总共走K步

开始暴力递归:

        假设当前位置在(x,y)处(任一位置),已经走了一部分,还剩下rest步需要走,目标位置是(a,b),那么在当前位置处一共八种可能性:(x+1,y+2)、(x+2,y+1)、(x+2,y-1)、(x+1,y-2)、(x-1,y-2)、(x-2,y-1)、(x-2,y+1)、(x-1,y+2),可以画一个坐标轴方便理解一点。

        考虑边界:当前位置(x,y)的八种可能性,如果跳出棋盘喃?那么则返回0种方法。

        递归退出条件:当剩余步数为0,并且刚好到达目标位置,退出递归,或者边界越界。

    /**
     * 当前位置(x,y);目标位置(a,b);还有rest步需要走
     * 棋盘规模:10*9
     *
     * @return 返回走完rest步到达目标位置的方法数
     */
    public static 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 p1 = process(x + 1, y + 2, rest - 1, a, b);
        int p2 = process(x + 2, y + 1, rest - 1, a, b);
        int p3 = process(x + 2, y - 1, rest - 1, a, b);
        int p4 = process(x + 1, y - 2, rest - 1, a, b);
        int p5 = process(x - 1, y - 2, rest - 1, a, b);
        int p6 = process(x - 2, y - 1, rest - 1, a, b);
        int p7 = process(x - 2, y + 1, rest - 1, a, b);
        int p8 = process(x - 1, y + 2, rest - 1, a, b);
        return p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8;
    }

 

 动态规划:

        由分析可知,递归方法中,影响步数的是x、y、剩余步数rest这三个,显而易见是一个三维dp数组。当rest==0的时候,刚好到目标位置时,方法数是1,其余时候全是0。那么先确定rest==0的时候(也就是第一层)dp数组的值。然后会发现每一层的rest都是依赖下一层(rest-1)的dp数组的值,那么可以从下到上完成dp数组。要注意判断(x,y)跳到下一步之后是否越界。所以先写一个判断是否越界的方法,如果从当前位置跳完之后越界,那么返回0,不越界的话从dp数组中找到对应的值返回。

代码如下:

    //判断是否越界的方法
    public static int pick(int[][][] dp, int x, int y, int rest) {
        if (x < 0 || x > 9 || y < 0 || y > 8) {
            return 0;
        }
        return dp[x][y][rest];
    }

 有了这个方法之后完成dp数组:

    public static int jumpDP(int a, int b, int K) {
        int[][][] dp = new int[10][9][K + 1];
        dp[a][b][0] = 1;
        for (int rest = 1; rest < K + 1; rest++) {//rest从倒数第二层开始写
            for (int x = 0; x < 10; x++) {
                for (int y = 0; y < 9; y++) {
                    int p1 = pick(dp, x + 1, y + 2, rest - 1);
                    int p2 = pick(dp, x + 2, y + 1, rest - 1);
                    int p3 = pick(dp, x + 2, y - 1, rest - 1);
                    int p4 = pick(dp, x + 1, y - 2, rest - 1);
                    int p5 = pick(dp, x - 1, y - 2, rest - 1);
                    int p6 = pick(dp, x - 2, y - 1, rest - 1);
                    int p7 = pick(dp, x - 2, y + 1, rest - 1);
                    int p8 = pick(dp, x - 1, y + 2, rest - 1);
                    dp[x][y][rest] = p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8;
                }
            }
        }
        return dp[0][0][K];
    }

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值