动态规划----走一步看一步

动态规划

动态规划是把原问题分解为相对简单的子问题的方式求解复杂问题的方法。
动态规划常常适用于有重叠子问题和最优子结构性质的问题,动态规划方法所耗时间往往远少于朴素解法。
动态规划背后的基本思想非常简单。大致上,若要解一个给定问题,我们需要解其不同部分(即子问题),再根据子问题的解以得出原问题的解。动态规划往往用于优化递归问题,例如斐波那契数列,如果运用递归的方式来求解会重复计算很多相同的子问题,利用动态规划的思想可以减少计算量。

通常许多子问题非常相似,为此动态规划法试图仅仅解决每个子问题一次,具有天然剪枝的功能,从而减少计算量:一旦某个给定子问题的解已经算出,则将其记忆化存储,以便下次需要同一个子问题解之时直接查表。这种做法在重复子问题的数目关于输入的规模呈指数增长时特别有用。


题目解析

53. 最大子序和

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

思路

动态规划的是首先对数组进行遍历,当前最大连续子序列和为 sum,结果为 ans
如果 sum > 0,则说明 sum 对结果有增益效果,则 sum 保留并加上当前遍历数字
如果 sum <= 0,则说明 sum 对结果无增益效果,需要舍弃,则 sum 直接更新为当前遍历数字
每次比较 sum 和 ans的大小,将最大值置为ans,遍历结束返回结果
时间复杂度:O(n)
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;
    }
 public int maxSubArray(int[] nums) {
    
    
        int pre = 0, maxAns = nums[0];
        for (int x : nums) {
    
    
            pre = Math.max(pre + x, x);
            maxAns = Math.max(maxAns, pre);
        }
        return maxAns;
    }

121. 买卖股票的最佳时机

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。

思路

记录  今天之前买入的最小值 
计算  今天之前最小值买入,今天卖出的获利 ,也即 今天卖出的最大获利 
比较  每天的最大获利 ,取最大值即可
 public int maxProfit(int[] prices) {
    
    
        if(prices.length <= 1)
            return 0;
        int min = prices[0], max = 0;
        for(int i = 1; i < prices.length; i++) {
    
    
            min = Math.min(min, prices[i]);
            max = Math.max(max, prices[i] - min);
        }
        return max;
    }

416. 分割等和子集

给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

注意:

每个数组中的元素不会超过 100
数组的大小不会超过 200

示例 1:

输入: [1, 5, 11, 5]    输出: true
解释: 数组可以分割成 [1, 5, 5] 和 [11].

示例 2:

输入: [1, 2, 3, 5]    输出: false
解释: 数组不能分割成两个元素和相等的子集.
 public boolean canPartition(int[] nums) {
    
    
     int len = nums.length;
     // 题目已经说非空数组,可以不做非空判断
     int sum = 0;
     for (int num : nums) {
    
    
         sum += num;
     }
     // 特判:如果是奇数,就不符合要求
    if(sum%2!=0) return false;
     int target = sum / 2;
     // 创建二维状态数组,行:物品索引,列:容量(包括 0)
     boolean[][] dp = new boolean[len][target + 1];
     // 先填表格第 0 行,第 1 个数只能让容积为它自己的背包恰好装满
     if (nums[0] <= target) {
    
    
         dp[0][nums[0]] = true;
     }
     // 再填表格后面几行
     for (int i = 1; i < len; i++) {
    
    
         for (int j = 0; j <= target; j++) {
    
    
             // 直接从上一行先把结果抄下来,然后再修正
             dp[i][j] = dp[i - 1][j];
             if (nums[i] == j) {
    
    
                 dp[i][j] = true;
                 continue;
             }
             if (nums[i] < j) {
    
    
                 dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i]];
             }
         }
     }
     return dp[len - 1][target];
 }

回头去看看背包九讲再更新

猜你喜欢

转载自blog.csdn.net/zmykkd/article/details/113365671