算法练习:从暴力递归到动态规划1

题目一:

假设有排成一行的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\K0123456
00000000
10001040
200104013
30103090
410205014
50102050

因此我们只需要写出这张表,即可直接返回(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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

superzheng__

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值