动态规划习题总结

目录

一.最小花费爬楼梯

1.题目

2.图解

3.代码

(1)时间复杂度为O(N),空间复杂度为O(N)

(2)时间复杂度为O(N),空间复杂度为O(1)

二.买卖股票的最佳时机(1)

1.题目

2.图解

3.代码 

三.买卖股票的最佳时机(2)

1.题目

2.思路图解

3.代码

四.反转数位

1.题目

2.图解

3.代码

 五.最大正方形

1.题目

2.图解

3.代码

六.连续子数组的最大乘积

1.题目

2.图解

3.代码

 七.和为k的子数组

1.题目

2.图解

3.代码

八.和为k的最长连续子数组的长度

1.题目

2.图解

3.代码


一.最小花费爬楼梯

1.题目

给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。

你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。

请你计算并返回达到楼梯顶部的最低花费。

例子:

输入:cost = [1,100,1,1,1,100,1,1,100,1]
输出:6
解释:你将从下标为 0 的台阶开始。
- 支付 1 ,向上爬两个台阶,到达下标为 2 的台阶。
- 支付 1 ,向上爬两个台阶,到达下标为 4 的台阶。
- 支付 1 ,向上爬两个台阶,到达下标为 6 的台阶。
- 支付 1 ,向上爬一个台阶,到达下标为 7 的台阶。
- 支付 1 ,向上爬两个台阶,到达下标为 9 的台阶。
- 支付 1 ,向上爬一个台阶,到达楼梯顶部。
总花费为 6 。

2.图解

3.代码

(1)时间复杂度为O(N),空间复杂度为O(N)

    //使用最小花费爬楼梯
    public int minCostClimbingStairs(int[] cost) {
        //dp每个位置是cost当前位置的最小值,最后一个位置保存前面到顶部的最小值
        int[] dp = new int[cost.length+1];
        //相当于初始化可以从第一个或的第二个开始进行跳跃
        for(int i=2; i<=cost.length; i++) {
            dp[i] = Math.min(dp[i-1]+cost[i-1], dp[i-2]+cost[i-2]);//找前两个中的最小
        }
        return dp[cost.length];
    }

(2)时间复杂度为O(N),空间复杂度为O(1)

public int minCostClimbingStairs2(int[] cost) {
        //动态规划只与i-1和i-2有关,所以使用滑动数组
        int pre = 0;//相当于保存i-2的值
        int cur = 0;//相当于保存i-1的值
        int n = cost.length;
        for(int i=2; i<=n; i++) {
            int next = Math.min(cur+cost[i-1], pre+cost[i-2]);//保存前面的最小值
            pre = cur;
            cur = next;
        }
        return cur;//cur就相当于最后cost.length的位置
    }

二.买卖股票的最佳时机(1)

1.题目

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。

你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。

返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。

例子:

输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
     注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

2.图解

3.代码 

public int maxProfit(int[] prices) {
        int minValue = Integer.MAX_VALUE;//买进的最低价格
        int maxPrice = 0;//卖出的最高价格
        for(int i=0; i<prices.length; i++) {
            if(minValue > prices[i]) {
        //保存最低买进的价格
                minValue = prices[i];
//判断是否满足当前的价钱是否和最低价格的差是否比最大利益大
            }else if(prices[i]-minValue > maxPrice) {
                maxPrice = prices[i] - minValue;
            }
        }
        return maxPrice;
    }

 

三.买卖股票的最佳时机(2)

1.题目

给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。

在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。

返回 你能获得的 最大 利润 。

示例:

输入:prices = [1,2,3,4,5]
输出:4
解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4 。
     总利润为 4 。

2.思路图解

首先使用辅助二维数组,里面存储第i天的最大利润,然后将将买卖股票当天分为两种情况,

(1)一种是第i天不持有股票,这种又分为两种,一种是前一天不持有(i-1),一种是前一天持有,当天卖出。

所得的dp公式为dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1]+price[i]);

(2)另一种是第i天持有股票,再分为两种,一种是前一天持有,今天不卖,一种是前一天不持有,今天买入。

所得dp公式为:dp[i][1] = Math.max(dp[i-1][1], dp[i-1][0]-price[i]);

(3)最终返回最后一天不买入的最大利润(因为最后一天买入,利润肯定减少)

3.代码

    public int maxProfit(int[] prices) {
        int[][] dp = new int[prices.length][2];
        //dp[i][0] 表示第i天没持有股票的最大利润
        //dp[i][1] 表示第i天持有股票的最大利润
        dp[0][0] = 0;
        dp[0][1] = 0-prices[0];
        for(int i=1; i<prices.length; i++) {
            //每种情况又分为两种
            dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1]+prices[i]);
            dp[i][1] = Math.max(dp[i-1][1], dp[i-1][0]-prices[i]);
        }
        //最后一天不持有股票的最大利益大于比持有股票的最大利益
        return dp[prices.length-1][0];
    }

四.反转数位

1.题目

给定一个32位整数 num,你可以将一个数位从0变为1。请编写一个程序,找出你能够获得的最长的一串1的长度。

示例 1:

输入: num = 1775(11011101111)
输出: 8

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/reverse-bits-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

2.图解

3.代码

public int reverseBits(int num) {
        int cur = 0;//表示从前非零位置开始到当前位置1的个数
        int insert = 0;//表示插入1个1的最长1的数位
        int res = 0;
        for(int i=0; i<32; i++) {
            //说明第i位的二进制数非零
            if((num&(1<<i))!= 0){
                cur++;
                insert++;
            }else {
                insert = cur+1;//第二次就会将之前插入的1的最长置为上一个开始0开始的位置的长度
                cur = 0;//说明当前有0,cur重新开始
            }
            //取最大值
            res = Math.max(res, insert);
        }
        return res;
    }

 五.最大正方形

1.题目

给定一个由'0'和'1'组成的2维矩阵,返回该矩阵中最大的由'1'组成的正方形的面积,输入的矩阵是字符形式而非数字形式。

示例:

输入:[[1,0,1,0,0],[1,0,1,1,1],[1,1,1,1,1],[1,0,0,1,0]]

返回值:4

2.图解

3.代码

    public int solve (char[][] matrix) {
        // 判断数组是否符合要求
        if(matrix==null || matrix.length==0) {
            return 0;
        }
        int row = matrix.length;
        int col = matrix[0].length;
        int[][] dp = new int[row][col];
        int len = 0;
        //由于数组下标从零开始,需要i-1参与运算,所以先确定第0行和第0列
        for(int i=0; i<col; i++) {
            if(matrix[0][i] == '1') {
                dp[0][i] = 1;
            }
        }
        for(int i=0; i<row; i++) {
            if(matrix[i][0] == '1') {
                dp[i][0] = 1;
            }
        }
        //在dp数组中存放当前位置所能构成的正方形边长,然后与最大边长进行比较
        for(int i=1; i<row; i++) {
            for(int j=1; j<col; j++) {
                if(matrix[i][j]== '1'){
                    dp[i][j] =
                        Math.min(dp[i-1][j],Math.min(dp[i-1][j-1],dp[i][j-1]))+1;
                    if(dp[i][j]>len) {
                        len = dp[i][j];
                    }
                }
            }
        }
        return len*len;
    }

六.连续子数组的最大乘积

1.题目

输入一个长度为n的整型数组nums,数组中的一个或连续多个整数组成一个子数组。求所有子数组的乘积的最大值。

(1)子数组是连续的,且最小长度为1,最大长度为n

(2)长度为1的子数组,乘积视为其本身,比如[4]的乘积为4

(3)该题的数据保证最大的乘积不会超过int的范围,即不超过2^{32}-1232−1

示例:

输入:[3,2,-1,4]

返回值:6

说明:子数组[3,2]的乘积为6,[3,2,-1,4]的乘积为-24,[4]的乘积为4,故返回6

2.图解

3.代码

 public int maxProduct (int[] nums) {
        // write code here
        int n = nums.length;
        //使用dp数组保存当前元素存在的最大连续乘积
        int[] maxDp = new int[n];
        //使用minDp是因为存在负数,可能之前的最小负数和之后的负数乘积变为最大值
        //minDp保存的是以当前元素结尾的最小连续子数组乘积
        int[] minDp = new int[n];
        maxDp[0] = nums[0];
        minDp[0] = nums[0];
        //保存连续数组的最大值乘积值
        int res = nums[0];
        //求前面连续数组乘积与当前元素乘积,当前元素中的最大最小值
        for(int i=1; i<n; i++) {
            maxDp[i] = Math.max(maxDp[i-1]*nums[i], Math.max(minDp[i-1]*nums[i], nums[i]));
            minDp[i] = Math.min(maxDp[i-1]*nums[i], Math.min(minDp[i-1]*nums[i], nums[i]));
            //maxDp【i】保存是以当前元素结尾的最大连续乘积
            res = Math.max(res, maxDp[i]);
        }
        return res;
    }

 七.和为k的子数组

1.题目

给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的子数组的个数 

输入:nums = [1,2,3], k = 3
结果为:(1,2) (3)
输出:2

2.图解

3.代码

 public int subarraySum(int[] nums, int k) {
        Map<Integer,Integer> map = new HashMap<>();
        //首先在map中存放一个(0,1),这里代表的是从i=0到j的数组和满足条件
        map.put(0,1);
        //统计到当前位置的和
        int sum = 0;
        //最终结果
        int res =0;
        for(int i=0; i<nums.length; i++) {
            sum += nums[i];
            //sum指的是从0到i的数组和,而sum-k指的是当前位置到前面某一位置是否存在和为k的数组和
            if(map.containsKey(sum-k)) {
                //这里说明存在,就将出现的次数加起来
                res += map.get(sum-k);
            }
            //如果到当前位置的数组和已经在之前存在,那么就将之前和的次数加1
            //因为下一次计算到该位置,是从i到j的子数组和
            map.put(sum, map.getOrDefault(sum,0)+1);
        }
        return res;
    }

八.和为k的最长连续子数组的长度

1.题目

给定一个无序数组 arr , 其中元素可正、可负、可0。给定一个整数 k ,求 arr 所有连续子数组中累加和为k的最长连续子数组长度。保证至少存在一个合法的连续子数组。[1,2,3]的连续子数组有[1,2],[2,3],[1,2,3] ,但是[1,3]不是。

示例:

输入:[1,-2,1,1,0],0

和为0的数组有【1,-2,1】,【0】,最长的长度为3

返回值:3

2.图解

3.代码

 public int maxlenEqualK (int[] arr, int k) {
        // write code here
        int sum = 0;
        //res保存数组最大长度
        int res = 0;
        //map的键保存的是从0到i之间的是数组和,思路也是通过从i到j之间的连续数组和
        Map<Integer,Integer> map = new HashMap<>();
        //当从初始位置开始的时候,且数组下标总比长度少了一个,从-1开始,刚好是连续数组区间的长度
        map.put(0, -1);
        for(int i=0; i<arr.length; i++) {
            sum += arr[i];
            //保存从i到j之间是否存在指定的连续数组和的结果
            int tmp = sum-k;
            if(map.containsKey(tmp)) {
                //每次找到一个满足要求的数组和,就找最大长度
                res = Math.max(res, i-map.get(tmp));
            }
            if(!map.containsKey(sum)) {
                //如果map中没有当前和,就添加
                map.put(sum, i);
            }
        }
        return res;
    }

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值