动态规划专题1

斐波那契数

https://leetcode.cn/problems/fibonacci-number/submissions/

这种递归很容易想到,就可以直接得出动态方程

class Solution {
    public int fib(int n) {
        return func(n);
    }

    public int func(int n){
        if (n == 0){
            return 0;
        }

        if (n == 1){
            return 1;
        }

        return func(n - 1) + func(n - 2);
    }

    public int dp(int n){
        if (n == 0){
            return 0;
        }
        if (n == 1){
            return 1;
        }

        int[] nums = new int[n + 1];
        nums[0] = 0;
        nums[1] = 1;
        for (int i = 2; i < nums.length; i++) {
            nums[i] = nums[i - 1] + nums[i - 2];
        }

        return nums[nums.length -1];
    }
}

爬楼梯

https://leetcode.cn/problems/climbing-stairs/

与上面那题差不多就不展示了。

最小代价楼梯

https://leetcode.cn/problems/min-cost-climbing-stairs/submissions/

思路:步步最优递归的规则,从最后三步开始,最后两步肯定是可以直接走完,那么我们在倒数第三步就可以选择小的那种情况看走1步还是2步。一直递归就可。

也是有递归再动态规划,但是在题有时间的限制,递归过不了,我们就必须改为动态规划。

class Solution1 {
    //递归超时,我们可以尝试动态规划
    public int minCostClimbingStairs(int[] cost) {
        if(cost.length <= 2){
            return cost[0] > cost[1] ? cost[1] : cost[0];
        }

        //选择0 1开,选择走一步或者两步
        return Math.min(func(cost,0),func(cost,1));
    }

    public int func(int[] cost,int index){

        if (index == cost.length - 1 || index == cost.length - 2){
            return cost[index];
        }

        return Math.min(cost[index] + func(cost,index + 1),cost[index] +func(cost,index + 2));
    }

    public int minCostClimbingStairsByDp(int[] cost){
        if(cost.length <= 2){
            return cost[0] > cost[1] ? cost[1] : cost[0];
        }

        int[] dp = new int[cost.length];

        dp[cost.length - 1] = cost[cost.length - 1];
        dp[cost.length - 2] = cost[cost.length - 2];

        for (int i = dp.length - 3; i >= 0; i--) {
            dp[i] = Math.min(cost[i] + dp[i + 1],cost[i] + dp[i + 2]);
        }

        return dp[0] > dp[1] ? dp[1] : dp[0];
    }
}

不同路劲

https://leetcode.cn/problems/unique-paths-i/

思路:只能向下和向右走两种可能,但他来到右边和下边的边界,那么他就只能一直走也就是只有一条路,递归思路很简单

class Solution10 {
    //m是y n是x
    public int uniquePaths(int m, int n) {
        //一条线只有一种可能
        if (m == 1 || n == 1){
            return 1;
        }

        return func(m,n,0,0);
    }

    public int func(int m,int n,int x,int y){
        if (m == y + 1 || n == x + 1){
            return 1;
        }

        return func(m,n,x + 1,y)
                + func(m,n,x,y + 1);

    }

    public int dp(int m,int n){
        if (m == 1 || n == 1){
            return 1;
        }
        int[][] dp = new int[m][n];

        for (int i = 0; i < dp[0].length; i++) {
            dp[dp.length - 1][i] = 1;
        }

        for (int i = 0; i < dp.length; i++) {
            dp[i][dp[0].length - 1] = 1;
        }

        for (int i = dp.length - 2; i >= 0; i--) {
            for (int j = dp[0].length - 2; j >= 0; j--) {
                dp[i][j] = dp[i + 1][j] + dp[i][j + 1];
            }
        }

        return dp[0][0];
    }
}

不同路劲2

https://leetcode.cn/problems/unique-paths-ii/

思路:这个和上一个差不多,只是多个障碍,所以难度也稍微大了一点,

这个在最后不能直接右边界和下边界取1,因为他有可能在前面还有障碍导致这条路走不通。

class Solution11 {
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        if (obstacleGrid[0][0] == 1){
            return 0;
        }
        return func(obstacleGrid,0,0);
    }

    //递归
    public int func(int[][] nums,int m,int n){
        //到达
        if (m == nums.length - 1 && n == nums[0].length - 1){
            return  1;
        }

        //在下边界和右边界
        if (m == nums.length - 1 && n < nums[0].length - 1){
            if (nums[m][n + 1] == 1){
                return 0;
            }else {
                return func(nums, m, n + 1);
            }
        }

        if (m < nums.length - 1 && n == nums[0].length - 1){
            if (nums[m + 1][n] == 1){
                return 0;
            }else {
                return func(nums, m + 1, n);
            }
        }

        
        if (nums[m + 1][n] == 1 && nums[m][n + 1] == 1){
            return 0;
        }

        if (nums[m + 1][n] == 1){
            return func(nums, m, n + 1);
        }

        if (nums[m][n + 1] == 1){
            return func(nums, m + 1, n);
        }

        return func(nums, m, n + 1)
                + func(nums, m + 1, n);
    }


    //动态规划
    public int dp(int[][] nums){

        if (nums[0][0] == 1){
            return 0;
        }
        int[][] dp = new int[nums.length][nums[0].length];

        //有可能在最后一行或者一列,其中有一个有障碍,那么我们就取他后面的为1
        int hang = 0;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i][nums[0].length - 1] == 1){
                hang = i;
            }
        }

            for (int i = hang; i < nums.length; i++) {
                dp[i][nums[0].length - 1] = 1;
            }


        hang = 0;
        for (int i = 0; i < nums[0].length; i++) {
            if (nums[nums.length - 1][i] == 1){
                hang = i;
            }
        }


            for (int i = hang; i < nums[0].length; i++) {
                dp[nums.length - 1][i] = 1;
            }

        for (int i = dp.length - 2; i >= 0; i--) {
            for (int j = dp[0].length - 2; j >= 0; j--) {
                if (nums[i + 1][j] == 1 && nums[i][j + 1] == 0){
                    dp[i][j] = dp[i][j + 1];
                }
                else if(nums[i][j + 1] == 1 && nums[i + 1][j] == 0){
                    dp[i][j] = dp[i + 1][j];
                }
                else if (nums[i][j + 1] == 1 && nums[i + 1][j] == 1){
                    dp[i][j] = 0;
                } else {
                    dp[i][j] = dp[i + 1][j] + dp[i][j + 1];
                }
            }
        }

        return dp[0][0];
    }
}

不同路劲3

https://leetcode.cn/problems/unique-paths-iii/

如果对上面两题很熟悉,那么这题也会很简单,但是他这里要走过所有是1的格子,所以最好在返回结果的时候要对结果进行遍历判断。

而且这题是可以进行上下左右走的,所以递归次数也就多了,对于这题的话可能数量样本较小,递归的时间超过了100%,也就没有写

动态规划了。

class Solution12 {
    public int uniquePathsIII(int[][] grid) {
        int[][] startAndEnd = getStartAndEnd(grid);
        boolean[][] isGone = new boolean[grid.length][grid[0].length];
        for (int i = 0; i < isGone.length; i++) {
            for (int j = 0; j < isGone[0].length; j++) {
                if (grid[i][j] == -1){
                    isGone[i][j] = true;
                }
            }
        }
        return func(grid,startAndEnd[0][0],startAndEnd[0][1],startAndEnd[1][0],startAndEnd[1][1],isGone);
    }


    public int func(int[][] grid,int y,int x,int resY,int resX,boolean[][] isGone){

        if (!inArea(grid,y,x) || grid[y][x] == -1 || isGone[y][x]){
            return 0;
        }

        if (grid[y][x] == 2){
            boolean a = true;
            isGone[resY][resX] = true;
            for (int i = 0; i < grid.length; i++) {
                for (int j = 0; j < grid[0].length; j++) {
                    if (!isGone[i][j]){
                        a = false;
                        break;
                    }
                }
            }
            isGone[resY][resX] = false;
            if (!a){
                return 0;
            }

            return 1;
        }

        isGone[y][x] = true;

        int sum = func(grid, y - 1, x, resY, resX, isGone)
                + func(grid, y + 1, x, resY, resX, isGone)
                + func(grid, y, x - 1, resY, resX, isGone)
                + func(grid, y, x + 1, resY, resX, isGone);

        //回溯撤销
        isGone[y][x] = false;

        return sum;
    }

    //是否在范围内
    public boolean inArea(int[][] grid,int y,int x){
        if (x < 0 || y < 0
            ||  x >= grid[0].length || y >= grid.length){
            return false;
        }else {
            return true;
        }
    }

    //获取出发点和结束点 出发点(res[0][0],res[0][1]) 结束点(res[1][0],res[1][1])
    public int[][] getStartAndEnd(int[][] nums){
        int[][] res = new int[2][2];
        for (int i = 0; i < nums.length; i++) {
            for (int j = 0; j < nums[0].length; j++) {
                if (nums[i][j] == 1){
                    res[0][0] = i;
                    res[0][1] = j;
                }

                if (nums[i][j] == 2){
                    res[1][0] = i;
                    res[1][1] = j;
                }
            }
        }
        return res;
    }
}

整数的拆分

https://leetcode.cn/problems/integer-break/submissions/

这里是有可以大于3就可以把它们拆开,比如2拆开肯定是1*1比原来小,就没有必要拆了

,比如3拆开2*1也比3小也就没必要拆了。

这里的递归严重超时,所以必须使用动态规划。

class Solution3 {
    public int integerBreak(int n) {
        //为了满足k>=2,而后面2和3都是不需要分的因为2分了只能是1*1,3分了只能是2*1
        if (n == 2){
            return 1;
        }

        if (n == 3){
            return 2;
        }
        return func(n);
    }

    public int func(int n){
        //2再分的话就变成1了那就会更加小了
        if (n == 2 || n == 3){
            return n;
        }
        //这几种情况不用分了,以为再分n 就小于2了

        int max = 1;
        //递归次数可以判断出为n/2 比如4
        for (int i = 0; i < n/2; i++) {
            //如果下面两个数还可以再分就继续递归 (i+1)*(n - i - 1)
            int n1 = func(i + 1);
            int n2 = func(n - i - 1);
            max = Math.max(max,n1*n2);
        }
        return max;
    }

    //动态规划
    public int dp(int n){
        int[] dp = new int[n];
        if (n == 2){
            return 1;
        }

        if (n == 3){
            return 2;
        }

        dp[0] = 1;
        dp[1] = 2;
        dp[2] = 3;

        for (int i = 3; i < dp.length; i++) {
            for (int j = 0; j < (i + 1)/2; j++) {
                dp[i] = Math.max(dp[j]* dp[i - j - 1],dp[i]);
            }
        }

        return dp[n - 1];
    }
}

不同的二叉搜索树

https://leetcode.cn/problems/unique-binary-search-trees/submissions/

这里也是主要从左右子树获取它们的次数,每次节点个数-1

递归方式的话刚好不超时,可以用,但是5%击败率就有点低,动态规划的话就100%。

class Solution4 {
    public int numTrees(int n) {
        return func2(n);
    }

    //实现一个子树能够有多少分支
    public int func2(int n){
        if (n == 1 || n == 0){
            return 1;
        }
        if (n == 2){
            return n;
        }

        int sum = 0;
        for (int i = 0; i < n; i++) {
            sum+= func2(i) * func2(n - 1 - i);
        }

        return sum;
    }

    public int dp(int n){
        if (n == 1 || n == 0){
            return 1;
        }
        if (n == 2){
            return n;
        }
        int[] dp = new int[n + 1];
        dp[0] = 1;
        dp[1] = 1;
        dp[2] = 2;

        for (int i = 3; i <= n; i++) {
            for (int j = 0; j < i; j++) {
                dp[i] += dp[j] *  dp[i - 1 - j];
            }
        }
        return dp[n];
    }
}

分割和子集问题

https://leetcode.cn/problems/partition-equal-subset-sum/

额,这道题开始优点卡主了不知道怎么用初始化dp数组,我用另一种方式,也刚好通过了,就是把所有sum的可能都加到set中,直接看target在不在就行了。

public boolean canPartition(int[] nums) {
        if (nums.length  == 1) { // 只有一个,无法分解
            return false;
        }
        int sum = 0;
        for (int i=0;i<nums.length;i++) {
            sum += nums[i];
        }
        if (sum % 2 == 1) { // 和不能是奇数
            return false;
        }
        int target = sum / 2;

        Set<Integer> cacheSum = new HashSet(); // 存储所有可能的和
        for (int num : nums) {
            Set<Integer> tmpSum = new HashSet();
            if (num == target) {
                return true;
            }
            if (num < target) {
                tmpSum.add(num);
            }

            for (int historySum : cacheSum) {
                int sum2 = historySum + num;
                if (sum2 == target) {
                    return true;
                }
                if (sum2 < target) {
                    tmpSum.add(sum2);
                }
            }
            cacheSum.addAll(tmpSum);

        }
        return false;
    }

下面是动态规划的,这里的递归严重超时

这里有一个典型的背景,就是0和1背包问题

class Solution5 {
  

    //二维数组
    public boolean dp(int[] nums){
        if (nums.length == 1){
            return false;
        }

        int sum = 0;
        for (int i = 0; i < nums.length; i++) {
            sum += nums[i];
        }

        if(sum % 2 == 1){
            return false;
        }

        int target = sum / 2;

        //如果为奇数的话不可能得出结果

        boolean[][] dp = new boolean[nums.length][target + 1];

        //现在就是完整的01背包问题了 装target个先初始化第一行 因为第一个数字如果小于target的话,肯定可以把他给装满
        if (nums[0] <= target){
            dp[0][nums[0]] = true;
        }

        for (int i = 1; i < nums.length; i++) {
            for (int j = 0; j < dp[0].length; j++) {
                if (j > nums[i]){
                    //看看上一行有无可以加起来满足这个背包的
                    dp[i][j] = dp[i - 1][j] | dp[i - 1][j - nums[i]];
                }else {
                    //如果之前就已经成功的就直接赋值
                    dp[i][j] = dp[i - 1][j] | dp[i][j];//相当dp[i][j] = dp[i - 1][j];
                }
            }
        }

        return dp[nums.length - 1][target];
    }


    //一维数组
    public boolean dp2(int[] nums) {
        if (nums.length == 1){
            return false;
        }

        int sum = 0;
        for (int i = 0; i < nums.length; i++) {
            sum += nums[i];
        }

        if(sum % 2 == 1){
            return false;
        }

        int target = sum / 2;

        //如果为奇数的话不可能得出结果

        boolean[] dp = new boolean[target + 1];

        //现在就是完整的01背包问题了 装target个先初始化第一行 因为第一个数字如果小于target的话,肯定可以把他给装满
        if (nums[0] <= target){
            dp[nums[0]] = true;
        }

        for (int i = 1; i < nums.length; i++) {
            for (int j = dp.length - 1; j >= nums[i]; j--) {
                //如果当前背包能装的数量大于他,看看前面有无能够装的
                //看看上一行有无可以加起来满足这个背包的
                dp[j] = dp[j] | dp[j - nums[i]];
            }
        }

        return dp[target];
    }

最后一块石头重量2

https://leetcode.cn/problems/last-stone-weight-ii/submissions/

思路和上面那题差不多,也是把求总和,分成两堆,两堆最接近一半,可以直接得出答案。使用一位数组优化较大

class Solution7 {
    //二维数组
    public int lastStoneWeightII(int[] stones) {
        if (stones.length == 1){
            return stones[0];
        }
        int sum = 0;
        for (int i = 0; i < stones.length; i++) {
            sum += stones[i];
        }

        int target = sum / 2;

        int[][] dp = new int[stones.length][target + 1];

        for (int i = 0; i < target + 1; i++) {
            if (i >= stones[0]){
                dp[0][i] = stones[0];
            }
        }

        for (int i = 1; i < stones.length; i++) {
            for (int j = 0; j < dp[0].length; j++) {
                if (j < stones[i]){
                    dp[i][j] = dp[i - 1][j];
                }else {
                    dp[i][j] = Math.max(dp[i - 1][j],dp[i - 1][j - stones[i]] + stones[i]);
                }
            }
        }

        return sum - target - dp[stones.length - 1][target];
    }


    //一维滚动数组
    public int lastStoneWeightII2(int[] stones) {
        if (stones.length == 1){
            return stones[0];
        }
        int sum = 0;
        for (int i = 0; i < stones.length; i++) {
            sum += stones[i];
        }

        int target = sum / 2;

        int[] dp = new int[target + 1];



        for (int i = 0; i < stones.length; i++) {
            for (int j = dp.length - 1; j >= stones[i]; j --) {
                dp[j] = Math.max(dp[j],dp[j - stones[i]] + stones[i]);
            }
        }

        return sum - dp[target] * 2;
    }
}

最后一块石头重量1

https://leetcode.cn/problems/last-stone-weight-i/submissions/

直接使用大根堆能快速解决

class Solution6 {
    public int lastStoneWeight(int[] stones) {
        if (stones.length == 1){
            return stones[0];
        }

        PriorityQueue<Integer> integers = new PriorityQueue<>(stones.length, new Comparator<Integer>() {


            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });


        for (int i = 0; i < stones.length; i++) {
            integers.add(stones[i]);
        }

        while (integers.size() >= 2){
            Integer n1 = integers.poll();
            Integer n2 = integers.poll();
            integers.add(Math.abs(n1 - n2));
        }

        return integers.poll();
    }
}

目标和

https://leetcode.cn/problems/target-sum/

如果这题使用递归的话就会很简单,而且题目也能过,如果在竞赛之类等短时间内的建议先用递归这种思路简单,时间快速的方法。

本题的话,也只有两种可能,一种是加这个数,一种是减这个数,自己画一下递归树就可以。

    int res = 0;
    public int findTargetSumWays2(int[] nums, int target) {
        if (nums.length == 1){
            if (nums[0] == Math.abs(target))
                return 1;
            else
                return 0;
        }
        func(nums,target,0,0);
        return res;
    }
    public void func(int[] nums,int target,int index,int sum){
        if (index == nums.length){
            if (sum == target){
                res++;
            }
                return;
        }

        func(nums, target, index + 1, sum + nums[index]);
        func(nums, target, index + 1, sum - nums[index]);
    }

下面是我自己的笨动态规划,和官方的差挺多的,时间肯定比暴力递归的要快很多,一个是6ms一个500ms,但是空间复杂度要高一点。

当然还可以进行优化。

public int findTargetSumWays(int[] nums, int target) {
        if (nums.length == 1){
            if (nums[0] == Math.abs(target))
                return 1;
            else
                return 0;
        }

        int sum = 0;
        for (int i = 0; i < nums.length; i++) {
            sum += nums[i];
        }

        if (target > sum){
            return 0;
        }

        int[][] dp = new int[nums.length][sum * 2 + 1];

        for (int i = dp[0].length / 2,j = sum - 1; i >= 0; i--,j--) {
            if (nums[0] == sum - i){
                if (nums[0] == 0){
                    dp[0][i] = 2;
                }else {
                    dp[0][i]++;
                }
            }
        }

        for (int i = dp[0].length / 2 + 1; i <= dp[0].length; i++) {
            if (nums[0] == i - sum){
                if (nums[0] == 0){
                    dp[0][i] = 2;
                }else {
                    dp[0][i]++;
                }
            }
        }

        for (int i = 0; i < dp.length - 1; i++) {
            for (int j = 0; j < dp[0].length; j++) {
                if (dp[i][j] != 0){
                    if (j - nums[i + 1] >= 0){
                        dp[i + 1][j - nums[i + 1]] += dp[i][j];
                    }

                    if (j + nums[i + 1] <= dp[0].length - 1) {
                        dp[i + 1][j + nums[i + 1]] += dp[i][j];
                    }
                }
            }
        }


        return dp[nums.length - 1][sum + target];
    }

最终优化

class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        int sum = 0;
        for (int i = 0; i < nums.length; i++) sum += nums[i];
        if ((target + sum) % 2 != 0) return 0;
        int size = (target + sum) / 2;
        if(size < 0) size = -size;
        int[] dp = new int[size + 1];
        dp[0] = 1;
        for (int i = 0; i < nums.length; i++) {
            for (int j = size; j >= nums[i]; j--) {
                dp[j] += dp[j - nums[i]];
            }
        }
        return dp[size];
    }
}

不考虑nums[i]的情况下,填满容量为j的背包,有dp[j]种方法。

那么考虑nums[i]的话(只要搞到nums[i]),凑成dp[j]就有dp[j - nums[i]] 种方法。

例如:dp[j],j 为5,

  • 已经有一个1(nums[i]) 的话,有 dp[4]种方法 凑成 dp[5]。
  • 已经有一个2(nums[i]) 的话,有 dp[3]种方法 凑成 dp[5]。
  • 已经有一个3(nums[i]) 的话,有 dp[2]中方法 凑成 dp[5]
  • 已经有一个4(nums[i]) 的话,有 dp[1]中方法 凑成 dp[5]
  • 已经有一个5 (nums[i])的话,有 dp[0]中方法 凑成 dp[5]

dp[j] += dp[j - nums[i]]

一和零

https://leetcode.cn/problems/ones-and-zeroes/

这里我开始是使用了三维数组的dp,效率确实比较慢,而且代码也比较难度,过程确实比上面的一维和二维要难

public int findMaxForm(String[] strs, int m, int n) {
        int[][][] dp = new int[strs.length][m + 1][n + 1];

        int[] mAndN = getMAndN(strs[0]);
        //初始化 第一个的
        for (int i = mAndN[0]; i <= m; i++) {
            for (int j = mAndN[1]; j <= n; j++) {
                dp[0][i][j] = 1;
            }
        }

        for (int i = 1; i <= strs.length - 1; i++) {
            int[] mAndN1 = getMAndN(strs[i]);
            for (int j = 0; j <= m; j++) {
                for (int k = 0; k <= n; k++) {
                    if (k < mAndN1[1] || j < mAndN1[0]){
                        dp[i][j][k] = dp[i - 1][j][k];
                    }else {
                        dp[i][j][k] = Math.max(dp[i - 1][j][k],dp[i - 1][j - mAndN1[0]][k - mAndN1[1]] + 1);
                    }
                }
            }
        }

        return dp[strs.length - 1][m][n];
    }
    
    
      public int[] getMAndN(String str){
        char[] chars = str.toCharArray();
        int m = 0;
        int n = 0;
        for (int i = 0; i < chars.length; i++) {
            if (chars[i] == '1'){
                n++;
            }else {
                m++;
            }
        }
        return new int[]{m,n};
    }

下面是我自己进行二维优化的

class Solution {
    public int findMaxForm(String[] strs, int m, int n) {
        int[][] dp = new int[m + 1][n + 1];
        int[] mAndN = getMAndN(strs[0]);
        //初始化 第一个的
        for (int i = mAndN[0]; i <= m; i++) {
            for (int j = mAndN[1]; j <= n; j++) {
                dp[i][j] = 1;
            }
        }

        for (int i = 1; i <= strs.length - 1; i++) {
            int[] mAndN1 = getMAndN(strs[i]);
            for (int j = m; j >= mAndN1[0]; j--) {
                for (int k = n; k >= mAndN1[1]; k--) {
                    if (k >= mAndN1[1] && j >= mAndN1[0]){
                        dp[j][k] = Math.max(dp[j][k],dp[j - mAndN1[0]][k - mAndN1[1]] + 1);
                    }
                }
            }
        }
        return dp[m][n];
    }

    public int[] getMAndN(String str){
        char[] chars = str.toCharArray();
        int m = 0;
        int n = 0;
        for (int i = 0; i < chars.length; i++) {
            if (chars[i] == '1'){
                n++;
            }else {
                m++;
            }
        }
        return new int[]{m,n};
    }
}

兑换零钱1

https://leetcode.cn/problems/coin-change/submissions/

class Solution9 {
    HashMap<Integer,Integer> map = new HashMap();
    public int coinChange(int[] coins, int amount) {
        return func(coins,amount);
    }


    public int func(int[] coins ,int resM){

        if (resM == 0){
            return 0;
        }

        if (resM < 0){
            return -1;
        }

        if (map.containsKey(resM)){
            return map.get(resM);
        }

        int min = Integer.MAX_VALUE;
        for (int i = 0; i < coins.length; i++) {
            int res = func(coins,resM - coins[i]);
            if (res >= 0 && res < min){
                min = res + 1;
            }
        }

        map.put(resM,(min == Integer.MAX_VALUE) ? -1 : min);

        return map.get(resM);
    }

    public int dp(int[] coins ,int amount){
        if (amount == 0){
            return 0;
        }


        int[] dp = new int[amount + 1];

        for (int i = 0; i < dp.length; i++) {
            dp[i] = Integer.MAX_VALUE;
        }

        dp[0] = 0;
        for (int i = 0; i < coins.length; i++) {
            for (int j = coins[i]; j < amount + 1; j++) {
                if (dp[j - coins[i]] != Integer.MAX_VALUE){
                    dp[j] = Math.min(dp[j],dp[j - coins[i]] + 1);
                }
            }
        }

        return dp[amount] == Integer.MAX_VALUE ? -1 : dp[amount];
    }
}

兑换零钱2

https://leetcode.cn/problems/coin-change-2/submissions/

class Solution10 {

    public int change(int amount, int[] coins) {
        return func(coins,amount,amount,0);
    }
	//递归暴力搜索
    public int func(int[] coins,int resA,int target,int index){

        if (resA == 0){
            return 1;
        }

        if (resA < 0 ){
            return 0;
        }

        //减枝条
        if (index >= coins.length || coins[index] > resA){
            return 0;
        }

        int sum = 0;

        for (int i = 0,j = 0; i <= resA; i+= coins[index],j++) {
            sum += func(coins,resA - coins[index] * j,target,index + 1);
        }

        return sum;
    }

    public int dp(int amount, int[] coins){
        //1,创建数组
        int[] dp = new int[amount + 1];

        //初始化 个体amount为0的都设置为1,因为都只有一种可能就是不用加,然后第一行就是个体第一个硬币设置
        dp[0] = 1;


        for (int i = 0; i < coins.length; i++) {
            for (int j = coins[i]; j < dp.length; j++) {
                dp[j] += dp[j - coins[i]];
            }
        }

        return dp[amount];
    }
}

完全平方数

https://leetcode.cn/problems/perfect-squares/

class Solution12 {
    public int numSquares(int n) {
        int[] nums = new int[(int) Math.sqrt(n)];
        for (int i = 0; i < nums.length; i++) {
            nums[i] = i + 1;
        }
        int[] dp = new int[n + 1];
        dp[0] = 0;
        for (int i = 1; i < dp.length; i++) {
            dp[i] = Integer.MAX_VALUE;
        }

        for (int i = 0; i < nums.length; i++) {
            int num = (int) Math.pow(nums[i],2);
            for (int j =num; j < dp.length; j++) {
                dp[j] = Math.min(dp[j],dp[j - num] + 1);
            }
        }

        return dp[n];
    }

    
    //下面是递归暴力搜索
    public int numSquares2(int n){
        int[] nums = new int[(int) Math.sqrt(n)];
        for (int i = 0; i < nums.length; i++) {
            nums[i] = i + 1;
        }

        return func(nums,n,0,0);
    }
    public int func(int[] nums,int n,int nowNum,int numNum){
        if (nowNum > n){
            return -1;
        }
        if (nowNum == n){

            return numNum;
        }

        int min = Integer.MAX_VALUE;
        for (int i = 0; i < nums.length; i++) {
            int num = (int)Math.pow(nums[i],2);
            int func = func(nums, n, nowNum + num,numNum + 1);
            //减枝
            if (func == -1){
                break;
            }else {
                min = Math.min(min,func);
            }
        }

        return min;
    }
}

打家劫舍1

递归和动态规划

class Solution241 {
    public int rob1(int[] nums) {

        if (nums.length == 1){
            return nums[0];
        }

        int[] dp = new int[nums.length];
        //第一间房间最大只能偷自己
        dp[0] = nums[0];

        dp[1] = nums[0] > nums[1] ? nums[0] : nums[1];

        for (int i = 2; i < nums.length - 1; i++) {
            dp[i] = Math.max(dp[i - 2] + nums[i],dp[i - 1]);
        }
        return dp[dp.length - 1];
    }

    public int rob(int[] nums) {
        if (nums.length == 1){
            return nums[0];
        }

        if (nums.length == 2){
            return nums[0] > nums[1] ? nums[0] : nums[1];
        }

        int func = func(nums, 0, nums[0]);
        func = Math.max(func,func(nums,1,nums[1]));

        return func;
    }

    public int func(int[] nums,int index,int sum){
        if (index == nums.length - 1 || index == nums.length - 2){
            return sum;
        }

        if (index > nums.length - 1){
            return sum;
        }

        //走i + 2  走 i + 3
        int max = func(nums, index + 2, sum + nums[index + 2]);

        if (index + 3 <= nums.length - 1){
            max = Math.max(func(nums, index + 3, sum + nums[index + 3]),max);
        }

        return max;
    }
}

买入股票最佳时机1

class Solution14 {
    public int maxProfit(int[] prices) {
        func(prices,0,0,0);
        return max;
    }

    int max = 0;

    //是否买入
    public void func(int[] prices,int index,int isBuy,int sum){
       if (index == prices.length){
           max = Math.max(max,sum);
           return;
       }

        //如果买入了 看看今天是不是最大 然后继续比下一个
        if (isBuy == 1){
            max = Math.max(max,sum + prices[index]);
            func(prices, index + 1, isBuy, sum);
            return;
        }

        //如果没买入 可以选择今天买,也可以选择不买
        func(prices, index + 1, 1, sum - prices[index]);
        func(prices, index + 1, isBuy, sum );
    }

    public int dp(int[] prices){
        //dp[0][i] 第i天买入的最低价格  dp[1][i] 第i天卖出的最大利润
        int[][] dp = new int[2][prices.length];

        dp[0][0] = prices[0];

        for (int i = 1; i < dp[0].length; i++) {
            dp[0][i] = Math.min(dp[0][i - 1],prices[i]);
            dp[1][i] = Math.max(dp[1][i - 1],prices[i] - dp[0][i - 1]);
        }

        return dp[1][prices.length - 1];
    }
}

nc(nums, index + 2, sum + nums[index + 2]);

    if (index + 3 <= nums.length - 1){
        max = Math.max(func(nums, index + 3, sum + nums[index + 3]),max);
    }

    return max;
}

}






买入股票最佳时机1

```java
class Solution14 {
    public int maxProfit(int[] prices) {
        func(prices,0,0,0);
        return max;
    }

    int max = 0;

    //是否买入
    public void func(int[] prices,int index,int isBuy,int sum){
       if (index == prices.length){
           max = Math.max(max,sum);
           return;
       }

        //如果买入了 看看今天是不是最大 然后继续比下一个
        if (isBuy == 1){
            max = Math.max(max,sum + prices[index]);
            func(prices, index + 1, isBuy, sum);
            return;
        }

        //如果没买入 可以选择今天买,也可以选择不买
        func(prices, index + 1, 1, sum - prices[index]);
        func(prices, index + 1, isBuy, sum );
    }

    public int dp(int[] prices){
        //dp[0][i] 第i天买入的最低价格  dp[1][i] 第i天卖出的最大利润
        int[][] dp = new int[2][prices.length];

        dp[0][0] = prices[0];

        for (int i = 1; i < dp[0].length; i++) {
            dp[0][i] = Math.min(dp[0][i - 1],prices[i]);
            dp[1][i] = Math.max(dp[1][i - 1],prices[i] - dp[0][i - 1]);
        }

        return dp[1][prices.length - 1];
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值