算法笔记——动态规划

2020-04-14  本文已影响0人  yaco

换钱的方法数

问题描述

给定数组arr,arr中所有的值都为正数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个整数aim代表要找的钱数,求换钱有多少种方法。
【举例】
arr=[5,10,25,1],aim=0。组成0元的方法有1种,就是所有面值的货币都不用。所以返回1。
arr=[5,10,25,1],aim=15。组成15元的方法有6种,分别为3张5元、1张10元+1张5元、1张10元+5张1元、10张1元+1张5元、2张5元+5张1元和15张1元。所以返回6。
arr=[3,5],aim=2。任何方法都无法组成2元。所以返回0。

暴力递归思路
暴力递归代码
    //+++++++++++++++++++++++++++++++++++方法一: 暴力递归的解法++++++++++++++++++++++++++++++++++++++++++++
    public static int coins1(int[] arr, int aim) {
        if (arr == null || arr.length == 0 || aim < 0) {
            return 0;
        }
        return process1(arr, 0, aim);
    }

    public static int process1(int[] arr, int index, int aim) {
        int res = 0;
        if (index == arr.length) {
            res = aim == 0 ? 1 : 0;
        } else {
            for (int i = 0; arr[index] * i <= aim; i++) {
                res += process1(arr, index + 1, aim - arr[index] * i);
            }
        }
        return res;
    }
暴力递归改动态规划
    // +++++++++++++++++++++++++方法二: 改版暴力递归,从后往前遍历+++++++++++++++++++++++++++++++++++++++++++++
    public static int coinsOther(int[] arr, int aim) {
        if (arr == null || arr.length == 0 || aim < 0) {
            return 0;
        }
        return processOther(arr, arr.length - 1, aim);
    }

    public static int processOther(int[] arr, int index, int aim) {
        int res = 0;
        if (index == -1) {
            res = aim == 0 ? 1 : 0;
        } else {
            for (int i = 0; arr[index] * i <= aim; i++) {
                res += processOther(arr, index - 1, aim - arr[index] * i);
            }
        }
        return res;
    }

纸牌博弈问题

问题描述

【题目】
给定一个整型数组arr,代表数值不同的纸牌排成一条线。玩家A和玩家B依次拿走每张纸牌,规定玩家A先拿,玩家B后拿,但是每个玩家每次只能拿走最左或最右的纸牌,玩家A和玩家B都绝顶聪明。请返回最后获胜者的分数。
【举例】
arr=[1,2,100,4]。开始时玩家A只能拿走1或4。如果玩家A拿走1,则排列变为[2,100,4],接下来玩家B可以拿走2或4,然后继续轮到玩家A。如果开始时玩家A拿走4,则排列变为[1,2,100],接下来玩家B可以拿走1或100,然后继续轮到玩家A。玩家A作为绝顶聪明的人不会先拿4,因为拿4之后,玩家B将拿走100。所以玩家A会先拿1,让排列变为[2,100,4],接下来玩家B不管怎么选,100都会被玩家A拿走。玩家A会获胜,
分数为101。所以返回101。arr=[1,100,2]。开始时玩家A不管拿1还是2,玩家B作为绝顶聪明的人,都会把10拿走。玩家B会获胜,分数为100。所以返回100。

思路分析
暴力递归代码如下
    //++++++++++++++++++++++++++++++++暴力递归求解++++++++++++++++++++++++++++++++++++++
    public static int win1(int[] arr) {
        if (arr == null || arr.length == 0) {
            return 0;
        }
        // 先选的最大和后选的最大值中求出最大的结果
        return Math.max(f(arr, 0, arr.length - 1), s(arr, 0, arr.length - 1));
    }

    // 先选的方法
    public static int f(int[] arr, int i, int j) {
        if (i == j) {
            return arr[i];
        }
        // 可以先选i位置的元素,后选[i+1,j]位置上的元素
        // 也可以先选j位置上的元素,然后后选[i,j-1]位置上的元素
        return Math.max(arr[i] + s(arr, i + 1, j), arr[j] + s(arr, i, j - 1));
    }

    // 后选的方法
    public static int s(int[] arr, int i, int j) {
        // 如果数组中只有一个元素,而且是后选,那么肯定直接返回0
        if (i == j) {
            return 0;
        }
        return Math.min(f(arr, i + 1, j), f(arr, i, j - 1));
    }
暴力递归改动态规划
    public static int win2(int[] arr) {
        if (arr == null || arr.length == 0) {
            return 0;
        }
        int[][] f = new int[arr.length][arr.length];
        int[][] s = new int[arr.length][arr.length];
        for (int j = 0; j < arr.length; j++) {
            f[j][j] = arr[j];
            for (int i = j - 1; i >= 0; i--) {
                f[i][j] = Math.max(arr[i] + s[i + 1][j], arr[j] + s[i][j - 1]);
                s[i][j] = Math.min(f[i + 1][j], f[i][j - 1]);
        }
        }
        return Math.max(f[0][arr.length - 1], s[0][arr.length - 1]);
    }


机器人路径问题

问题描述

指定一个机器人,可以在0 - n的线性网格中左右移动,如果机器人在0位置,只能向右移动,如果机器人在n位置,只能向左移动,否则可以选择向两边移动。
【问题】
现在给你网格长度n, 机器人初始位置m, 机器人可以移动的步数p, 求最终机器人落在k位置有多少种情况?

暴力递归思路
暴力递归代码
    //+++++++++++++++++++++++++++++++++---阿里笔试题----+++++++++++++++++++++++++++++++++++++++++

    /**
     * 问题描述:给定一个机器人,在0-n长度的格子中行走,起始位置为m,移动的步数为p
     * 如果机器人在0位置,那么只能往右走
     * 如果机器任在n位置,那么只能往左走
     * 如果机器人在中间位置,那么即可以往左走也可以往右走
     *
     * 问: 机器任走p步,最终落在k位置有多少种移动的方式
     * @param n
     * @param m
     * @param k
     * @return
     */
    public static int stepNums(int n, int m, int k, int p){
        // 如果越界了,直接返回0
        if(m > n || m < 0) return 0;
        // 如果步数用完了,且当前机器人在k的位置,则返回1
        if(p == 0) return k == m ? 1 : 0;
        // 机器向左走的方式
        return stepNums(n, m + 1, k, p - 1) + stepNums(n, m - 1, k, p - 1);
    }
暴力递归改动态规划
    //++++++++++++++++++++--------改动态规划---------++++++++++++++++++++
    public static int stepNums2(int n, int m, int k, int p){
        if(m > n || m < 0 || p < 1 || k < 0 || k > m){
            return 0;
        }
        int[][] dp = new int[p+1][n+1];
        for (int i = 0; i <= p; i++) {
            for (int j = 0; j <= n; j++) {
                if(i == 0){
                    dp[i][j] = j == m ? 1 : 0;  // 出发点是m, 所以初始化m点的位置是1
                }else if(j == 0){
                    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[p][k];
    }
上一篇下一篇

猜你喜欢

热点阅读