LeetCode动态规划

    技术2022-07-10  94

    动态规划的的四个解题步骤是:

    定义子问题 写出子问题的递推关系 确定 DP 数组的计算顺序 空间优化(可选)

    状态的定义,最重要的是确定转移方程 状态转移方程的定义 状态的初始化 返回结果

    LeetCode53. 最大子序和

    给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

    示例: 输入: [-2,1,-3,4,-1,2,1,-5,4] 输出: 6

    解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

    算法: 动态规划的是首先对数组进行遍历,当前最大连续子序列和为 sum,结果为 ans; 如果 sum > 0,则说明 sum 对结果有增益效果,则 sum 保留并加上当前遍历数字; 如果 sum <= 0,则说明 sum 对结果无增益效果,需要舍弃,则 sum 直接更新为当前遍历数字; 每次比较 sum 和 ans的大小,将最大值置为ans,遍历结束返回结果 时间复杂度:O(n)。

    class Solution { public int maxSubArray(int[] nums) { int ans = nums[0]; int sum = 0; for(int num: nums) { if(sum > 0) { sum += num; } else { sum = num; } ans = Math.max(ans, sum); } return ans; } }

    LeetCode121. 买卖股票的最佳时机

    给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

    如果你最多只允许完成一笔交易(即买入和卖出一支股票一次),设计一个算法来计算你所能获取的最大利润。

    注意:你不能在买入股票前卖出股票。

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

    示例 2: 输入: [7,6,4,3,1] 输出: 0 解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

    动态规划方法解决这个问题附上链接: https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/solution/wu-chong-shi-xian-xiang-xi-tu-jie-121-mai-mai-gu-p/

    暴力法: 我们需要找出给定数组中两个数字之间的最大差值(即,最大利润)。此外,第二个数字(卖出价格)必须大于第一个数字(买入价格)。 形式上,对于每组 ii 和 jj(其中 j > ij>i)我们需要找出max(prices[j]−prices[i])。

    public class Solution { public int maxProfit(int prices[]) { int maxprofit = 0; for (int i = 0; i < prices.length - 1; i++) { for (int j = i + 1; j < prices.length; j++) { int profit = prices[j] - prices[i]; if (profit > maxprofit) maxprofit = profit; } } return maxprofit; } }

    LeetCode198. 打家劫舍

    你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。

    给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

    示例 1: 输入:[1,2,3,1] 输出:4 解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。 偷窃到的最高金额 = 1 + 3 = 4 。

    示例 2: 输入:[2,7,9,3,1] 输出:12 解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。 偷窃到的最高金额 = 2 + 9 + 1 = 12 。

    算法: 动态规划方程:dp[n] = MAX( dp[n-1], dp[n-2] + num ) 由于不可以在相邻的房屋闯入,所以在当前位置 n 房屋可盗窃的最大值,要么就是 n-1 房屋可盗窃的最大值,要么就是 n-2 房屋可盗窃的最大值加上当前房屋的值,二者之间取最大值 举例来说:1 号房间可盗窃最大值为 33 即为 dp[1]=3,2 号房间可盗窃最大值为 44 即为 dp[2]=4,3 号房间自身的值为 22 即为 num=2,那么 dp[3] = MAX( dp[2], dp[1] + num ) = MAX(4, 3+2) = 5,3 号房间可盗窃最大值为 55 时间复杂度:O(n)O(n),nn 为数组长度

    class Solution { public int rob(int[] nums) { int len = nums.length; if(len == 0) return 0; int[] dp = new int[len + 1]; dp[0] = 0; dp[1] = nums[0]; for(int i = 2; i <= len; i++) { dp[i] = Math.max(dp[i-1], dp[i-2] + nums[i-1]); } return dp[len]; } }

    LeetCode746. 使用最小花费爬楼梯

    数组的每个索引作为一个阶梯,第 i个阶梯对应着一个非负数的体力花费值 costi。 每当你爬上一个阶梯你都要花费对应的体力花费值,然后你可以选择继续爬一个阶梯或者爬两个阶梯。 您需要找到达到楼层顶部的最低花费。在开始时,你可以选择从索引为 0 或 1 的元素作为初始阶梯。

    示例 1: 输入: cost = [10, 15, 20] 输出: 15 解释: 最低花费是从cost[1]开始,然后走两步即可到阶梯顶,一共花费15。

    示例 2: 输入: cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1] 输出: 6 解释: 最低花费方式是从cost[0]开始,逐个经过那些1,跳过cost[3],一共花费6。

    方法: 计算花费 f[i] 有一个清楚的递归关系:f[i] = cost[i] + min(f[i+1], f[i+2])。我们可以使用动态规划来实现。 算法: 当我们要计算 f[i] 时,要先计算出 f[i+1] 和 f[i+2]。所以我们应该从后往前计算 f。 在第 i 步,让 f1,f2 为 f[i+1],f[i+2] 的旧值,并将其更新为f[i],f[i+1] 的新值。当我们从后遍历 i 时,我们会保持这些更新。在最后答案是 min(f1, f2)。

    class Solution { public int minCostClimbingStairs(int[] cost) { int f1 = 0, f2 = 0; for (int i = cost.length - 1; i >= 0; --i) { int f0 = cost[i] + Math.min(f1, f2); f2 = f1; f1 = f0; } return Math.min(f1, f2); } }
    Processed: 0.038, SQL: 9