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

动态规划-最大子数组和

最大子数组和

题目描述

给你一个整数数组 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;elsereturn max(big, sum - sma);}
};

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • STM32的CRC校验(基于HAL库)
  • c++面向对象程序设计中的二义性及解决办法--郭妍论文
  • Electron 项目实战 03: 实现一个截图功能
  • Spark框架
  • 【kubernetes】持久化存储 —— PV / PVC
  • 打开mdk的configuration wizard界面
  • Qt:玩转QPainter序列九(文本,文本框,填充)
  • SpringBoot Web请求响应
  • 极盾故事|某金融租赁机构应用数据保护新策略:“动态脱敏”“二次授权”
  • Trm理论 2(Word2Vec)
  • 使用AI写WebSocket知识是一种怎么样的体验?
  • 【C++ Qt day5】
  • Docker 安装FileBeat、Elasticsearch及Kibana详细步骤
  • git查看代码提交记录
  • python使用selenium,实现简单爬虫功能
  • CSS盒模型深入
  • httpie使用详解
  • Javascript设计模式学习之Observer(观察者)模式
  • PHP变量
  • 对话:中国为什么有前途/ 写给中国的经济学
  • 复习Javascript专题(四):js中的深浅拷贝
  • 关于Java中分层中遇到的一些问题
  • 记一次用 NodeJs 实现模拟登录的思路
  • 你真的知道 == 和 equals 的区别吗?
  • 我与Jetbrains的这些年
  • 正则表达式
  • ionic入门之数据绑定显示-1
  • # Java NIO(一)FileChannel
  • #NOIP 2014#Day.2 T3 解方程
  • $NOIp2018$劝退记
  • (1)(1.8) MSP(MultiWii 串行协议)(4.1 版)
  • (8)STL算法之替换
  • (C语言)共用体union的用法举例
  • (day6) 319. 灯泡开关
  • (SpringBoot)第二章:Spring创建和使用
  • (二)c52学习之旅-简单了解单片机
  • (附源码)spring boot车辆管理系统 毕业设计 031034
  • (附源码)ssm高校运动会管理系统 毕业设计 020419
  • (附源码)ssm智慧社区管理系统 毕业设计 101635
  • (紀錄)[ASP.NET MVC][jQuery]-2 純手工打造屬於自己的 jQuery GridView (含完整程式碼下載)...
  • (数据结构)顺序表的定义
  • (四)stm32之通信协议
  • (五)IO流之ByteArrayInput/OutputStream
  • (一)、python程序--模拟电脑鼠走迷宫
  • (转)为C# Windows服务添加安装程序
  • (转载)从 Java 代码到 Java 堆
  • ***检测工具之RKHunter AIDE
  • .net core开源商城系统源码,支持可视化布局小程序
  • .NET delegate 委托 、 Event 事件,接口回调
  • .net 按比例显示图片的缩略图
  • .NET8使用VS2022打包Docker镜像
  • .net中我喜欢的两种验证码
  • :class的用法及应用
  • @Validated和@Valid校验参数区别
  • [ 网络通信基础 ]——网络的传输介质(双绞线,光纤,标准,线序)