Algorithm 27: From violent recursion to dynamic programming (2)

The previous question was relatively easy, and the following one is more difficult.

Assume that there are N positions in a row, recorded as 1~N, and N must be greater than or equal to 2

At the beginning, the robot is at the M position (M must be one of 1~N)

If the robot comes to position 1, then the next step is to go right to position 2;

If the robot comes to position N, then the next step can only go left to position N-1;

If the robot comes to the middle position, then the next step can go left or right;

It is stipulated that the robot must take K steps, and how many ways are there to finally reach the position P (P is also one of 1~N)

Given four parameters N, M, K, P, returns the method number.

Problem-solving ideas:

1. If the robot is at position 1, it can only move from left to right

2. If the robot is in position N, it can only move from right to left

3. If the robot is in the middle, it can either go right or left

The question now is how many ways to go back, that is, as long as we can reach the destination and the number of steps is enough, we can walk at will.

When encountering this kind of problem, drawing is the best way:

Suppose there are 6 positions in total, start from position 2, walk 5 steps, and reach position 5, how many ways are there in total?

Recursive method

package code03.Dynamic programming_07;

/**
 * Suppose there are N positions in a row, recorded as 1~N, N must be greater than or equal to 2
 * At the beginning, the robot is at the M position (M must be one of 1~N)
 * If the robot comes to position 1, then the next step can only go to the right to position 2;
 * If the robot comes to position N, then the next step can only go left to position N-1;
 * If the robot comes to the middle position, then the next step can go left or right;
 * It is stipulated that the robot must take K steps, and how many ways are there to finally reach the P position (P is also one of 1~N)
 * Given four parameters N, M, K, P, returns the number of methods.
 */
public class Robot_02 {
    /**
     *
     * @param mStart M start position
     * @param kSteps K steps
     * @param nAll N length
     * @param pAim P destination position
     * @return
     */
    public static int way1(int mStart, int kSteps, int nAll, int pAim)
    {
        return process(mStart, kSteps, nAll, pAim);
    }

    public static int process ( int mStart, int kSteps, int nAll, int pAim)
    {
        //The number of steps is 0, unable to walk
        if (kSteps == 0) {
           return mStart == pAim ? 1 : 0;
        }
        // only go right
        if (mStart == 1) {
            return process(mStart + 1, kSteps-1, nAll, pAim);
        }
        // only go left
        if (mStart == nAll) {
            return process(nAll-1, kSteps-1, nAll, pAim);
        }
        //In the middle position, you can go left or right, and you need to count the sum of going left and right
        return process(mStart + 1, kSteps-1, nAll, pAim)
                 + process( mStart -1, kSteps -1, nAll, pAim);
    }

    public static void main(String[] args) {
        System.out.println(way1(2,5,6,5));
    }
}

For the same problem, we can get:

At position 2, with 5 moves remaining, there are 5 moves

At position 3, there are 4 steps left, and there are 4 ways to move

At position 4, there are 3 steps left, and there are 3 ways to move

At position 5, there are 2 steps left, and there are 2 ways to move;

At position 6, there is 1 move left, and there is 1 way to move

There are repeated back and forth moves in the middle, which shows that we have repeated derivation results, so we naturally transition to the recursive + dynamic programming method for optimization.

Recursion + Dynamic Programming

package code03.Dynamic programming_07;

/**
 * Suppose there are N positions in a row, recorded as 1~N, N must be greater than or equal to 2
 * At the beginning, the robot is at the M position (M must be one of 1~N)
 * If the robot comes to position 1, then the next step can only go to the right to position 2;
 * If the robot comes to position N, then the next step can only go left to position N-1;
 * If the robot comes to the middle position, then the next step can go left or right;
 * It is stipulated that the robot must take K steps, and how many ways are there to finally reach the P position (P is also one of 1~N)
 * Given four parameters N, M, K, P, returns the number of methods.
 */
public class Robot_02_opt1 {
    /**
     * @param mStart M start position [0-nAll]
     * @param kSteps K steps
     * @param nAll N length
     * @param pAim P destination position
     * @return
     */
    public static int way1(int mStart, int kSteps, int nAll, int pAim)
    {
        /**
         * The position is 1-N, each position has a different way of walking, each position is the row, then the range of the row is 1-N
         * Since the java array subscript starts from 0, if you want to get the 1-N range, you need to add 1 to the length
         *
         * We put the remaining steps into columns, so that the remaining steps of each position can be observed intuitively
         */
        int[][] dp = new int[nAll + 1][kSteps + 1]; //The number of remaining steps is the abscissa, and the current position is the ordinate
        for (int i = 0; i < dp. length; i ++ ) {
            for(int j = 0; j <dp[i].length; j ++ ) {
                dp[i][j] = -1;
            }
        }
        int result = process(mStart, kSteps, nAll, pAim, dp);
        return process(mStart, kSteps, nAll, pAim, dp);
    }

    public static int process ( int mStart, int kSteps, int nAll, int pAim, int[][] dp)
    {
        if (dp[mStart][kSteps] != -1) {
            return dp[mStart][kSteps];
        }

        int result;
        if (kSteps == 0) {
            result = mStart == pAim ? 1 : 0;
        }
        // only go right
        else if (mStart == 1) {
            result = process(mStart + 1, kSteps-1, nAll, pAim, dp);
        }
        // only go left
        else if (mStart == nAll) {
            result = process(nAll-1, kSteps-1, nAll, pAim, dp);
        }
        //In the middle position, you can go left or right, and you need to count the sum of going left and right
        else {
            result = process(mStart + 1, kSteps-1, nAll, pAim, dp)
                     + process( mStart -1, kSteps -1, nAll, pAim, dp);
        }
        dp[mStart][kSteps] = result;

        return result;
    }

    public static void main(String[] args) {
        System.out.println(way1(2,5,6,5));
    }
}

Pure dynamic programming method

1. According to the recursive method, we know that if the number of remaining steps is 0, then if the starting position is exactly at the target position, it is considered as one way of moving. And it can be deduced that the first column of the two-dimensional array, the fifth row is 1, and the others are 0

if (kSteps == 0) {
   return mStart == pAim ? 1 : 0;
}
x x x x x x
0
0
0
0
1
0

2. According to the recursive method, if you are at the far left end, you can only go right; if you are at the far right end, you can only go left. At present, the first column of data has been deduced. So it needs to be deduced from the first column.

//Can only go to the right
if (mStart == 1) {
    return process(mStart + 1, kSteps-1, nAll, pAim);
}
// only go left
if (mStart == nAll) {
    return process(nAll-1, kSteps-1, nAll, pAim);
}

According to the recursive code, we know whether it is going left or right.

The second column of the second row, that is, dp[1][1], is the data of the previous column of the next row. The last column of the last row is the data of the previous column of the previous row. Derivation results”

3. According to the recursive method, if in the middle position. Need to get the cumulative value of the previous column of the previous row and the next row

//In the middle position, you can go left or right, and you need to count the sum of going left and right
return process(mStart + 1, kSteps-1, nAll, pAim)
         + process( mStart -1, kSteps-1, nAll, pAim);

deduce the result

x x x x x x
0 0
0
0
0
1
0 1
x x x x x x
0 0
0 0
0 0
0 1
1 0
0 1

By analogy, the final value obtained is:

Remaining 0 Remaining 1 Remaining 2 Remaining 3 Remaining 4 Remaining 5
Target position 0 x x x x x x
target position 1 0 0 0 0 1 0
target position 2 0 0 0 1 0 5
target position 3 0 0 1 0 4 0
target position 4 0 1 0 3 0 9
target position 5 1 0 2 0 5 0
target position 6 0 1 0 2 0 9

According to the topic, we know that the position is from 1 to N, that is to say, there is no position of 0. And the position of our table is used as a row, and the remaining steps are used as a column. Therefore, a position of 0 is ignored directly.

Suppose there are 6 positions in total, start from position 2, walk 5 steps, and reach position 5, how many ways are there in total?

dp[2][5] can get a result of 5, that is, 5 kinds of moves.

package code03.Dynamic programming_07;

/**
 * Suppose there are N positions in a row, recorded as 1~N, N must be greater than or equal to 2
 * At the beginning, the robot is at the M position (M must be one of 1~N)
 * If the robot comes to position 1, then the next step can only go to the right to position 2;
 * If the robot comes to position N, then the next step can only go left to position N-1;
 * If the robot comes to the middle position, then the next step can go left or right;
 * It is stipulated that the robot must take K steps, and how many ways are there to finally reach the P position (P is also one of 1~N)
 * Given four parameters N, M, K, P, returns the number of methods.
 */
public class Robot_02_opt2 {
    /**
     *
     * @param mStart M start position
     * @param kSteps K steps
     * @param nAll N length
     * @param pAim P destination position
     * @return
     */
    public static int way(int nAll, int mStart, int pAim, int kSteps)
    {
        if (nAll < 2 || mStart < 1 || mStart > nAll || pAim < 1 || pAim > nAll || kSteps < 1) {
            return -1;
        }

        /**
         * The position is 1-N, each position has a different way of walking, each position is the row, then the range of the row is 1-N
         * Since the java array subscript starts from 0, if you want to get the 1-N range, you need to add 1 to the length
         *
         * We put the remaining steps as a column, so that the remaining steps of each position can be observed intuitively.
         * Similarly, the length of the column also needs to be increased by 1.
         *
         * In fact, it doesn't matter what is listed here, even if it is 100. But if it is 100, it will deduce that
         * In the case of going 100, there is no need to waste resources. The best way is based on the actual business, you want me to deduce
         * A few steps, I will deduce a few steps to save resources
         */
        int[][] dp = new int[nAll + 1][kSteps + 1];
        //The number of remaining steps is 0, deduce which behavior corresponds to 1
        dp[pAim][0] = 1;
        //According to the remaining steps, deduce the result that each position can reach the target position
        for (int col = 1; col <= kSteps; col ++ )
        {
            //Each column value in row 2 corresponds to the previous column value in row 3
            dp[1][col] = dp[2][col-1];

            //Start from the second line and end after the penultimate line is executed
            for (int row = 2; row < nAll; row ++ )
            {
                //The middle row is equal to the sum of the previous column of the previous row and the next row
                dp[row][col] = dp[row-1][col-1] + dp[row+1][col-1];
            }
            //The last row, each column value corresponds to the value of the previous column of the previous row
            dp[nAll][col] = dp[nAll-1][col-1];
        }

        //According to the current position, the number of remaining steps. Different ways to get to the target position
        return dp[mStart][kSteps];
    }



    public static void main(String[] args) {
        System.out.println(way(6,2,5,5));
    }
}