演算法基礎提升學習3

橡皮筋兒發表於2021-12-09

暴力遞迴轉動態規劃

機器人到達指定位置

假設有排成一行的 N 個位置,記為 1~N,N 一定大於或等於 2。開始時機器人在其中的 M 位 置上(M 一定是 1~N 中的一個),機器人可以往左走或者往右走,如果機器人來到 1 位置, 那 麼下一步只能往右來到 2 位置;如果機器人來到 N 位置,那麼下一步只能往左來到 N-1 位置。 規定機器人必須走 K 步,最終能來到 P 位置(P 也一定是 1~N 中的一個)的方法有多少種。給 定四個引數 N、M、K、P,返回方法數。

轉換為動態規劃的思路

  1. 想出暴力遞迴的方法
  2. 根據暴力遞迴中變數的數量,設計表,表的範圍參考變數的範圍
  3. 在第二部的基礎上,尋找規律,比如那些列,那些行一開始就是可以確定的,然後找出正確方向順序來遍歷剛才第二部設計的表,將基本位置補全。
/**
 * @Author: 郜宇博
 * @Date: 2021/11/12 14:41
 */
public class RobotCounts {
    public static void main(String[] args) {
        long s1 = System.currentTimeMillis();
        //System.out.println(getRobotCounts1(7, 3, 30, 5));
        long s2 = System.currentTimeMillis();
        System.out.println(getRobotCounts2(7,3,5000,5));
        long s3 = System.currentTimeMillis();
        System.out.println(fuc3(7,3,1000000,5));
        long s4 = System.currentTimeMillis();
        System.out.println("s1="+(s2-s1));
        System.out.println("s2="+(s3-s2));
        System.out.println("s3="+(s4-s3));
    }
    //開始在M位置,走K步,最後到P,一共有1-N個位置
    public static int getRobotCounts1(int N,int M,int K,int P){
        return fuc(N,M,K,P);
    }
    public static int getRobotCounts2(int N,int M,int K,int P){
        //使用表儲存
        int [][]dp = new int[N+1][K+1];
        //初始化表,初始化時看可變引數的變化範圍
        for (int i = 0; i < N+1; i++) {
            for(int j = 0; j < K+1; j++){
                dp[i][j] = -1;
            }
        }
        return fuc1(N,M,K,P,dp);
    }
    public static int getRobotCounts3(int N,int M,int K,int P){
        //使用表儲存
        int [][]dp = new int[N+1][K+1];
        //初始化表,初始化時看可變引數的變化範圍
        for (int i = 0; i < N+1; i++) {
            for(int j = 0; j < K+1; j++){
                dp[i][j] = -1;
            }
        }
        return fuc1(N,M,K,P,dp);
    }
    /**
     * 方法一:暴力遞迴
     * @param N 一共有1-N個位置
     * @param cur 當前位置 M
     * @param rest 剩餘步 K
     * @param P 目標點
     * @return 當前情況下到P的可能數
     */
    public static int fuc(int N,int cur,int rest,int P){
        //base case
        if (rest == 0){
            //沒步數,看是否到終點
            return cur == P? 1:0;
        }
        //到兩邊了
        if (cur == 1){
            return fuc(N,2,rest-1,P);
        }
        if (cur == N){
            return fuc(N,N-1,rest-1,P);
        }
        //在中間
        return fuc(N,cur-1,rest-1,P) + fuc(N,cur+1,rest-1,P);
    }
    /**
     * 方法二:記憶儲存
     * @param N 一共有1-N個位置
     * @param cur 當前位置
     * @param rest 剩餘步
     * @param P 目標點
     * @return 當前情況下到P的可能數
     */
    public static int fuc1(int N,int cur,int rest,int P,int[][]dp){
        if (dp[cur][rest] != -1){
            return dp[cur][rest];
        }
        if (rest == 0){
            dp[cur][rest]= cur == P? 1:0;
            return dp[cur][rest];
        }
        else {
            if (cur == 1){
                dp[cur][rest]= fuc1(N,2,rest-1,P,dp);
            }
            else if (cur == N){
                dp[cur][rest]= fuc1(N,N-1,rest-1,P,dp);
            }
            else {
                dp[cur][rest]=fuc1(N,cur-1,rest-1,P,dp) + fuc1(N,cur+1,rest-1,P,dp);
            }
        }

        return dp[cur][rest];
    }
    /**
     * 方法三:動態規劃
     * @param N 一共有1-N個位置
     * @param M 當前位置
     * @param K 剩餘步
     * @param P 目標點
     * @return 當前情況下到P的可能數
     * dp[剩餘步數][當前位置]
     */
    public static int fuc3(int N,int M,int K,int P){
        int[][] dp = new int[K+1][N+1];
        dp[0][P] = 1;
        for (int i = 1; i <= K; i++){
            for (int j = 1; j <= N; j++){
                if (j == 1){
                    dp[i][j] = dp[i-1][j+1];
                }
                else if (j == N){
                    dp[i][j] = dp[i-1][j-1];
                }
                //中間
                else {
                    dp[i][j] = dp[i-1][j-1] + dp[i-1][j+1];
                }

            }
        }
        return dp[K][M];
    }

}

棋盤問題

馬從0,0位置出發到x,y,只能走step步,有多少種可能

相當於從x,y出發到0,0

/**
 * @Author: 郜宇博
 * @Date: 2021/11/12 17:56
 */
public class HorseCounts {
    public static void main(String[] args) {
        long s1 = System.currentTimeMillis();
        System.out.println(getHorseCounts1(7, 7, 8));
        long s2 = System.currentTimeMillis();
        System.out.println(getHorseCounts2(7, 7, 15));
        long s3 = System.currentTimeMillis();
        System.out.println("S1="+(s2-s1));
        System.out.println("S2="+(s3-s2));
    }
    public static int getHorseCounts1(int x,int y, int step){
        return f1(x,y,step);
    }
    public static int getHorseCounts2(int x,int y, int step){
        if (x < 0 || x > 8 || y < 0 || y > 9){
            return 0;
        }
        //儲存的為x,y到0,0可以走step不得所有可能數
        int[][][] dp = new int[9][10][step+1];
        dp[0][0][0] = 1;
        for (int k = 1; k <= step; k++){
            for (int i = 0; i < 9; i++){
                for (int j = 0; j < 10; j++){
                    //只和上一層有關,第0層初始狀態,第一層能由第0層推出。。。。
                    dp[i][j][k] += getValue(dp,i-1,j-2,k-1);
                    dp[i][j][k] += getValue(dp,i-2,j-1,k-1);
                    dp[i][j][k] += getValue(dp,i-2,j+1,k-1);
                    dp[i][j][k] += getValue(dp,i-1,j+2,k-1);
                    dp[i][j][k] += getValue(dp,i+1,j+2,k-1);
                    dp[i][j][k] += getValue(dp,i+2,j+1,k-1);
                    dp[i][j][k] += getValue(dp,i+2,j-1,k-1);
                    dp[i][j][k] += getValue(dp,i+1,j-2,k-1);
                }
            }
        }
        return dp[x][y][step];
    }

    private static int getValue(int[][][] dp, int x, int y, int step) {
        if (x < 0 || x > 8 || y < 0 || y > 9 || step < 0){
            return 0;
        }
        return dp[x][y][step];
    }

    /**
     * 從x,y出發,到0,0點的可能數
     */
    private static int f1(int x, int y, int step) {
        //走越界了
        if (x < 0 || x > 8 || y < 0 || y > 9){
            return 0;
        }
        if (step == 0){
            return (x == 0 && y == 0)?1:0;
        }else {
            return f1(x-1,y-2,step-1)+
                    f1(x-2,y-1,step-1)+
                    f1(x-2,y+1,step-1)+
                    f1(x-1,y+2,step-1)+
                    f1(x+1,y+2,step-1)+
                    f1(x+2,y+1,step-1)+
                    f1(x+2,y-1,step-1)+
                    f1(x+1,y-2,step-1);
        }
    }
}

湊錢可能數

[x,y,z,c]其中x,yz,c是人民幣面值,有任意張,aim為目標

問:湊齊aim的可能數

/**
 * @Author: 郜宇博
 * @Date: 2021/11/12 19:39
 */
public class MoneyCounts {
    public static void main(String[] args) {
        System.out.println(getMoneyCount(new int[]{2, 3, 4, 10},50));
        System.out.println(getMoneyCount1(new int[]{2, 3, 4, 10},50));
        System.out.println(getMoneyCount2(new int[]{2, 3, 4, 10},50));

    }
    public static int getMoneyCount(int[] arr, int aim){
        return f(0,aim,arr);
    }
    public static int getMoneyCount1(int[] arr, int aim){
        if (arr == null || arr.length == 0){
            return 0;
        }
        //dp[index,rest]
        int[][] dp = new int[arr.length+1][aim+1];
        dp[arr.length][0] = 1;
        //從下向上
        for (int index = arr.length-1 ; index >= 0; index--){
            for (int rest = 0; rest <= aim; rest++){
                for (int i = 0; arr[index]*i <= rest; i++){
                    dp[index][rest] += dp[index+1][rest-arr[index]*i];
                }
            }
        }
        return dp[0][aim];
    }
    public static int getMoneyCount2(int[] arr, int aim){
        if (arr == null || arr.length == 0){
            return 0;
        }
        //dp[index,rest]
        int[][] dp = new int[arr.length+1][aim+1];
        dp[arr.length][0] = 1;
        //從下向上
        for (int index = arr.length-1 ; index >= 0; index--){
            for (int rest = 0; rest <= aim; rest++){
                dp[index][rest] = dp[index+1][rest];
                if (rest-arr[index] >= 0){
                    dp[index][rest] += dp[index][rest-arr[index]];
                }
            }
        }
        return dp[0][aim];
    }
    public static int f(int index,int rest,int []arr){
        if (index == arr.length){
            return rest==0?1:0;
        }
        //有可用的錢,並且還沒湊齊aim
        int res = 0;
        //i代表張數,嘗試arr[index]位置的任意張數
        for (int i = 0; i*arr[index]<=rest ;i++){
            res += f(index+1,rest-arr[index]*i,arr);
        }
        return res;
    }
}

相關文章