当前位置: 首页 > article >正文

动态规划-最大子数组和

最大子数组和

题目描述

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。子数组是数组中的一个连续部分。

53. 最大子数组和 - 力扣(LeetCode)

示例 :

输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。

 解题思路

这个问题是动态规划的一个经典问题,也被称作“最大子序和问题”或“最大子数组和”。解决这个问题的思路是遍历数组,在遍历的过程中,我们维护一个当前遍历到的子数组的最大和,以及全局的最大和。每当我们遍历到一个新的元素时,我们考虑两种情况:

  1. 如果将当前元素加入之前的子数组会使得子数组的和变得更大,那么我们就更新当前子数组的和。
  2. 如果当前元素自己就是一个更大的子数组(即之前的子数组和小于0,加入当前元素还不如只包含当前元素),那么我们重新开始一个新的子数组,其和就是当前元素的值。

动态规划思路

在最大子序和问题中,我们想要找到一个具有最大和的连续子数组。这个问题可以分解为多个子问题:对于数组中的每个位置,我们需要知道以该位置结尾的最大子数组和是多少。

定义状态

我们定义一个数组dp,其中dp[i]表示以nums[i]结尾的连续子数组的最大和。注意,这里的定义稍微有些不同,因为我们实际上并不需要显式地存储整个dp数组(尽管在某些情况下这样做可以方便调试和理解),而是可以通过变量来维护当前的最大和(即dp数组中的最后一个有效值)以及全局的最大和。

状态转移方程

对于每个位置i(从1开始计数,假设dp[0] = nums[0]),我们需要根据前一个状态dp[i-1]和当前元素nums[i]来更新dp[i]。这里有两种情况:

  1. 如果dp[i-1] + nums[i] > nums[i],即加上当前元素后子数组的和变得更大,那么我们就应该继续扩展这个子数组,即dp[i] = dp[i-1] + nums[i]
  2. 否则,如果dp[i-1] + nums[i] <= nums[i],即加上当前元素后子数组的和没有变得更大(甚至可能变得更小),那么我们就应该重新开始一个新的子数组,只包含当前元素,即dp[i] = nums[i]

但是,在实际编程中,我们并不需要显式地创建一个dp数组来存储每个位置的状态,因为我们可以只通过几个变量来维护当前的最大和以及全局的最大和。

初始化
  • 全局最大和初始化为数组的第一个元素(或者一个足够小的数,但在这个问题中,由于子数组至少包含一个元素,所以初始化为第一个元素是合理的)。
  • 当前最大和也初始化为数组的第一个元素。
遍历数组

从数组的第二个元素开始遍历,对于每个元素,根据状态转移方程更新当前最大和,并检查是否需要更新全局最大和。

返回结果

遍历结束后,全局最大和就是我们要找的最大子数组和。

代码示例

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int n = nums.size();
        vector<int> dp(n, 0);
        dp[0] = nums[0];
        for (int i = 1; i < n; i++) {
            dp[i] = max(dp[i - 1] + nums[i], nums[i]);
        }

        int ret = dp[0];
        for (int i = 1; i < n; i++) {
            ret = max(ret, dp[i]);
        }
        return ret;
    }

};

环形子数组的最大和

题目描述

给定一个长度为 n 的环形整数数组 nums ,返回 nums 的非空 子数组 的最大可能和 。 环形数组 意味着数组的末端将会与开头相连呈环状。形式上, nums[i] 的下一个元素是 nums[(i + 1) % n] , nums[i] 的前一个元素是 nums[(i - 1 + n) % n] 。 子数组 最多只能包含固定缓冲区 nums 中的每个元素一次。形式上,对于子数组 nums[i], nums[i + 1], ..., nums[j] ,不存在 i <= k1, k2 <= j 其中 k1 % n == k2 % n 。

918. 环形子数组的最大和 - 力扣(LeetCode)

解题思路

为了解决这个问题,我们可以将问题分解为两个子问题:

  1. 非环形数组的最大子数组和:这是经典的Kadane算法问题,其中我们需要找到非环形数组中的最大子数组和。

  2. 环形数组中跨越首尾的最大子数组和:这可以通过计算原数组中去掉某个最小子数组后的最大剩余和来找到。换句话说,我们找到数组中的一个最小子数组(可能是连续的),然后从数组中去掉这个子数组,剩下的部分就是我们要找的可能跨越首尾的最大子数组。

核心思路分为两部分:

  1. 计算非环形情况下的最大子数组和
    • 使用 f 数组来记录以每个位置结尾的子数组的最大和。对于 f[i],它的值等于 max(f[i-1] + nums[i], nums[i]),即要么是当前元素自身,要么是前一个子数组的和加上当前元素(如果加上当前元素能使和更大)。
    • 遍历整个数组,同时更新 big 变量,记录到当前位置为止的最大子数组和。
  2. 计算环形情况下可能跨越首尾的最大子数组和
    • 考虑到环形特性,一个可能的最大子数组可能跨越数组的首尾。为了找到这种子数组,我们需要知道如果排除某个最小子数组后,剩余部分的和是否可能更大。
    • 使用 g 数组来记录以每个位置结尾的子数组的最小和(这里的“最小和”是为了后续计算方便,实际是为了找到可以排除的最小部分)。对于 g[i],它的值等于 min(g[i-1] + nums[i], nums[i]),即要么是当前元素自身,要么是前一个子数组的和加上当前元素(即使加上当前元素使和更小,因为我们要找的是最小和)。
    • 遍历整个数组,同时更新 sma 变量,记录到当前位置为止的最小子数组和。
    • 累加整个数组的元素到 sum 变量中,这个变量代表了整个数组的和。
    • 最后,比较两种情况:
      • 如果整个数组的和 sum 等于最小子数组和 sma,说明数组中所有元素都是负数,或者所有正数都已经被最小子数组所包含。此时,最大子数组和只能是 big(即非环形情况下的最大子数组和)。
      • 否则,可能的最大子数组和要么是 big(非环形情况),要么是 sum - sma(排除最小子数组后的剩余部分)。取两者中的较大值作为结果。

代码示例 

class Solution {
public:
    int maxSubarraySumCircular(vector<int>& nums) {
        int n = nums.size();
        vector<int> f(n, INT_MIN); // 最大
        vector<int> g(n, INT_MAX); // 最小
        f[0] = g[0] = nums[0];
        for (int i = 1; i < n; i++) {
            f[i] = max(f[i - 1] + nums[i], nums[i]);
            g[i] = min(g[i - 1] + nums[i], nums[i]);
        }

        int big = f[0];
        int sma = g[0];
        int sum = nums[0];
        for (int i = 1; i < n; i++) {
            big = max(big, f[i]);
            sma = min(sma, g[i]);
            sum += nums[i];
        }
        if (sum == sma)
            return big;
        else
            return max(big, sum - sma);
    }
};


http://www.kler.cn/a/288836.html

相关文章:

  • Spring Boot 自动配置:从 spring.factories 到 AutoConfiguration.imports
  • Animated Drawings:让纸上的角色动起来
  • 计算机网络 (10)网络层
  • 使用sam进行零样本、零学习的分割实践
  • 菜鸟带新鸟——基于EPlan2022的部件库制作(3D)
  • VS Code AI开发之Copilot配置和使用详解
  • [解决]Prometheus 与 Grafana进行组合,但是不显示数据与图像
  • 【王树森】Transformer模型(1/2): 剥离RNN,保留Attention(个人向笔记)
  • Java开发学习Kotlin 笔记
  • 每天学习一个基础算法之插入排序
  • 谷歌地图广告指南
  • P1438 无聊的数列
  • React 实现PDF预览(数据源使用文件流而不是url)
  • 哪些好用的待办事项清单值得推荐:待办任务清单app
  • (二十八)STL set(集合)
  • 前端vue中怎么判断接口请求返回的时长
  • 【量化交易的数学基础】文科生也能搞懂的线性代数基础:矩阵和向量的那些事儿
  • 学习日志29
  • 【IT工具】Windows下XMind安装教程【不要米】及常用快捷键
  • 翻译_Clock Domain Crossing Design
  • 【RSA】简单说说什么是RSA非对称加密
  • C++封装:栈、队列
  • Vue.js 模板语法详解:插值表达式与指令使用指南
  • 企业微信hook协议接口,聚合群聊客户管理工具开发
  • 有关Prompt Engineering(提示词工程)的一些总结
  • pypiserver 搭建