最大字段和问题 C++(穷举、分治法、动态规划)

问题描述

给定由n个整数(包含负整数)组成的序列a1,a2,…,an,求该序列子段和的最大值。规定当所有整数均为负值时定义其最大子段和为0

穷举法

最简单的方法就是穷举法,用一个变量指示求和的开始位置,一个变量指示结束位置,再一个变量指示当前要加和的位置,每一个开始位置对应n-i个结束位置,遍历一遍就能得到最大值

int maxSubArray(int a[], int n) {int maxsum = 0;for (int i = 0; i < n; i++) {//开始位置for (int j = i; j < n; j++) {//结束位置int nowsum = 0;for (int k = i; k <= j; k++) {nowsum += a[k];if (nowsum > maxsum)maxsum = nowsum;}}}return maxsum;
}

算法有三重循环,时间复杂性为O(n^3)

穷举法优化
当字段的开始下标确定后,要计算[i:j]的字段和可以利用上一次计算的[i:j-1]的字段和,加上a[j]就可以了

int maxSubArray2(int a[], int n) {int maxsum = 0;for (int i = 0; i < n; i++) {//开始位置int nowsum = 0;for (int j = i; j < n; j++) {//结束位置nowsum += a[j];if (nowsum > maxsum)maxsum = nowsum;}}return maxsum;
}

改进后的时间复杂度为O(n^2)

分治法

该问题也可以用分治法解决

分治策略思想如下:
将所给序列a[1:n]分成长度相同的两端a[1:n/2]a[n/2 +1:n],分别求出这两段的最大子段和,则整体序列a[1:n]的最大子段和有以下三种情况

  • a[1:n]的最大子段和与a[1:n/2]的最大子段和相同
  • a[1:n]的最大子段和与a[n/2 +1:n]的最大子段和相同
  • a[1:n]的最大子段和是a[1:n/2]最后一段加a[n/2 +1:n]最开始一段的和

前两种情况可以递归求得。对于第三种情况,可以发现,a[n/2]和a[n/2 +1]都在最大子段里,我们可以从a[n/2]向左、从a[n/2 +1]向右分别计算两个最大字段和s1和s2,s1+s2就是最大子段和

递归方程

MaxSum ( l o w , h i g h ) = { max ⁡ ( 0 , arr [ l o w ] ) if  l o w = h i g h max ⁡ { MaxSum ( l o w , m i d ) MaxSum ( m i d + 1 , h i g h ) CrossSum ( l o w , m i d , h i g h ) otherwise \text{MaxSum}(low, high) = \begin{cases} \displaystyle \max\left(0,\, \text{arr}[low]\right) & \text{if } low = high \\ \displaystyle \max \begin{cases} \text{MaxSum}(low, mid) \\ \text{MaxSum}(mid+1, high) \\ \text{CrossSum}(low, mid, high) \end{cases} & \text{otherwise} \end{cases} MaxSum(low,high)= max(0,arr[low])max MaxSum(low,mid)MaxSum(mid+1,high)CrossSum(low,mid,high)if low=highotherwise

代码

int maxSubArray3(int a[], int left, int right) {if (left == right)return a[left];int mid = (left + right) / 2;int maxleft = maxSubArray3(a, left, mid);int maxright = maxSubArray3(a, mid + 1, right);int maxleftsum = 0, maxrightsum = 0;int nowsum = 0;for (int i = mid; i >= left; i--) {nowsum += a[i];if (nowsum > maxleftsum)maxleftsum = nowsum;}nowsum = 0;for (int i = mid + 1; i <= right; i++) {nowsum += a[i];if (nowsum > maxrightsum)maxrightsum = nowsum;}return max(maxleft, max(maxright, maxleftsum + maxrightsum));
}

T ( n ) = { 2 T ( n 2 ) + O ( n ) n > c O ( 1 ) n ≤ c T(n)=\big \{^{O(1) \quad n\le c}_{2T(\frac{n}{2})+O(n) \quad n>c} T(n)={2T(2n)+O(n)n>cO(1)nc
根据master定理,我们得到
T ( n ) = O ( n l o g ) T(n)=O(nlog) T(n)=O(nlog)

比起穷举法的O(n^2)更优了

动态规划

设数组为 a[1..n],定义状态 b[i] 表示以 a[i] 结尾的子段的最大和,则最大字段和为 m a x b j max b_j maxbj

有递归方程:
b [ i ] = { 0 i = 0 ( 边界条件 ) max ⁡ ( b [ i − 1 ] + a [ i ] , a [ i ] ) i ≥ 1 b[i] = \begin{cases} 0 & i = 0 \quad (\text{边界条件}) \\ \max(b[i-1] + a[i],\, a[i]) & i \ge 1 \end{cases} b[i]={0max(b[i1]+a[i],a[i])i=0(边界条件)i1
全局最大子段和为所有 b[i] 中的最大值,并与0比较:
MaxSum = max ⁡ ( 0 , max ⁡ 1 ≤ i ≤ n b [ i ] ) \text{MaxSum} = \max\left(0,\, \max_{1 \le i \le n} b[i]\right) MaxSum=max(0,1inmaxb[i])

计算最优值

使用变量b记录此前最大字段和,如果为负数则当前最大和为a[i],如果为正数则最大和为b+a[i]
代码

int maxSubArray4(int a[], int n) {int b = 0;int maxsum = 0;for (int i = 0; i < n; i++) {if (b > 0)b += a[i];else b = a[i];if (b > maxsum)maxsum = b;}return maxsum;
}

构造最优解

使用两个变量startend记录最大字段的起始和结束位置

int maxSubArray4(int a[], int n, int* start, int* end) {int b = 0;int max_sum = 0;int current_start = 0;  // 当前子段起始位置*start = *end = -1;     // 默认无效索引for (int i = 0; i < n; i++) {if (b > 0) {b += a[i];}else {b = a[i];current_start = i;  // 重置起点}// 更新全局最大值及索引if (b > max_sum) {max_sum = b;*start = current_start;*end = i;}}// 处理全负数情况:返回0且不记录索引if (max_sum <= 0) {*start = *end = -1;return 0;}return max_sum;
}

时间负责度:O(n)

实例

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
//穷举法
int maxSubArray(int a[], int n) {int maxsum = 0;for (int i = 0; i < n; i++) {//开始位置for (int j = i; j < n; j++) {//结束位置int nowsum = 0;for (int k = i; k <= j; k++) {nowsum += a[k];if (nowsum > maxsum)maxsum = nowsum;}}}return maxsum;
}
//穷举法优化
int maxSubArray2(int a[], int n) {int maxsum = 0;for (int i = 0; i < n; i++) {//开始位置int nowsum = 0;for (int j = i; j < n; j++) {//结束位置nowsum += a[j];if (nowsum > maxsum)maxsum = nowsum;}}return maxsum;
}
//分治法
int maxSubArray3(int a[], int left, int right) {if (left == right)return a[left];int mid = (left + right) / 2;int maxleft = maxSubArray3(a, left, mid);int maxright = maxSubArray3(a, mid + 1, right);int maxleftsum = 0, maxrightsum = 0;int nowsum = 0;for (int i = mid; i >= left; i--) {nowsum += a[i];if (nowsum > maxleftsum)maxleftsum = nowsum;}nowsum = 0;for (int i = mid + 1; i <= right; i++) {nowsum += a[i];if (nowsum > maxrightsum) {maxrightsum = nowsum;}}return max(maxleft, max(maxright, maxleftsum + maxrightsum));
}
//动态规划
int maxSubArray4(int a[], int n, int* start, int* end) {int b = 0;int max_sum = 0;int current_start = 0;  // 当前子段起始位置*start = *end = -1;     // 默认无效索引for (int i = 0; i < n; i++) {if (b > 0) {b += a[i];}else {b = a[i];current_start = i;  // 重置起点}// 更新全局最大值及索引if (b > max_sum) {max_sum = b;*start = current_start;*end = i;}}// 处理全负数情况:返回0且不记录索引if (max_sum <= 0) {*start = *end = -1;return 0;}return max_sum;
}
int main() {int a[] = { 1, -2, 3, 10, -4, 7, 2, -5 };cout << maxSubArray(a, 8) << endl;cout << maxSubArray2(a, 8) << endl;cout << maxSubArray3(a, 0, 7) << endl;int start, end;cout << maxSubArray4(a, 8, &start, &end) << endl;cout <<"start:" << start << " " <<"end:"<< end << endl;return 0;
}

运行结果
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/73459.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何理解三极管截至区、放大区、饱和区

一、 三极管符号&#xff1a; NPN : PNP: 二、Vce、与Ic曲线图 1、截至区&#xff1a;ib很小的时候就是截至区。因为Ib很小的时候等价于Ub很小&#xff0c;Ub如果不足以达到0.7V PN结就不会导通&#xff0c;所以三极管就…

电脑上我的windows目录下,什么是可以删除的

在Windows系统目录&#xff08;通常是C:\Windows&#xff09;中&#xff0c;大部分文件和文件夹都是系统运行所必需的&#xff0c;随意删除可能导致系统崩溃或程序无法运行。不过&#xff0c;部分文件可以安全清理。以下是详细指南&#xff1a; 可安全清理的内容 临时文件&…

工作中遇到的spark SQL小问题:包含某个或某些字符的条件

今天又来总结工作中遇到的问题了&#xff0c;今天是SQL&#xff0c;spark引擎 需求描述&#xff0c;筛选渠道包含”线上化“的数据 也就是讨论where里面的这个筛选条件怎么写 一般起手都是 where QD like %线上化%‘ 学习了其他的写法: 1.INSTR函数 where INSTR(QD,&quo…

Git 命令操作完全指南

Git 是现代软件开发中不可或缺的分布式版本控制系统。它不仅能追踪代码变更&#xff0c;还能协调多人协作、管理项目历史。本文从核心概念入手&#xff0c;逐步深入讲解 Git 的基础与高级命令&#xff0c;结合实用场景&#xff0c;帮助您从入门到精通。 一、Git 核心概念 理解…

深入剖析带头循环双向链表的实现与应用

引言 场景描述 想象一个 环形地铁线路&#xff08;如深圳地铁11号线&#xff09;&#xff0c;这条线路首尾相连&#xff0c;列车可以顺时针或逆时针循环行驶。为了方便管理&#xff0c;地铁系统设置了一个 “虚拟调度中心”&#xff08;头节点&#xff09;&#xff0c;它不承…

DeepSeek Smallpond 在火山引擎 AI 数据湖的探索实践

资料来源&#xff1a;火山引擎-开发者社区 DeepSeek Smallpond 介绍 Smallpond 是一套由 DeepSeek 推出的 、针对 AI 领域&#xff0c;基于 Ray 和 DuckDB 实现的轻量级数据处理引擎&#xff0c;具有以下优点&#xff1a; 1.轻量级 2.高性能 3.支持规模大 4.无需运维 5.P…

Linux进程间的通信

进程间通信 1.进程间通信介绍2.匿名命名管道原理操作 1.进程间通信介绍 1.1 进程间通信目的&#xff1a;一个进程需要将他的数据发送给另一个进程&#xff0c;大家应该都多少接触过linux中的管道符"|"&#xff0c;这个符号就是用来多个命令执行&#xff0c;在Linux中…

直播预告 | TDgpt 智能体发布 时序数据库 TDengine 3.3.6 发布会即将开启

从海量监控数据&#xff0c;到工业、能源、交通等场景中实时更新的各类传感器数据&#xff0c;时序数据正在以指数级速度增长。而面对如此庞杂的数据&#xff0c;如何快速分析、自动发现问题、精准预测未来&#xff0c;成为企业数字化转型过程中的关键挑战。 TDengine 的答案是…

手撕FIO工具指南:从压测翻车到避坑实战

文章目录 手撕FIO工具指南&#xff1a;从压测翻车到避坑实战一、背景&#xff1a;一次FIO压测引发的惊魂夜二、FIO vs 其他IO工具&#xff1a;为何让人又爱又怕&#xff1f;三、安装指南&#xff1a;避开依赖地狱四、参数详解五、避坑指南&#xff1a;血泪经验总结六、安全压测…

智能汽车图像及视频处理方案,支持视频星轨拍摄能力

美摄科技作为智能汽车图像及视频处理领域的先行者&#xff0c;正以革新性的技术引领着行业的未来发展。美摄科技智能汽车图像及视频处理方案&#xff0c;一个集高效性、智能化、画质增强于一体的创新解决方案&#xff0c;旨在重塑智能汽车图像画质的新标准&#xff0c;并支持前…

B站左神算法课学习笔记(P7):图

目录 一、图的存储方式&#xff08;千奇百怪&#xff09; 1&#xff09;邻接表 2&#xff09;邻接矩阵 3&#xff09;其他 4&#xff09;推荐存储方式&#xff08;代码&#xff09; 二、图的遍历 &#xff08;1&#xff09;宽度优先遍历 &#xff08;2&#xff09;深度…

深度解析「前缀和」与「差分法」:高效算法的基石

深度解析前缀和与差分法&#xff1a;高效算法的基石 在计算机科学和数据处理领域&#xff0c;前缀和&#xff08;Prefix Sum&#xff09;与差分法&#xff08;Difference Method&#xff09;是两种基础且高效的算法技术。它们在处理数组的区间查询和区间修改操作时&#xff0c…

2-1 基本放大电路

放大的概念 mV →V mA→A 特征&#xff1a;放大功率&#xff08;电压与电流&#xff09;。 本质&#xff1a;能量在控制下的转换。&#xff08;外接供电电源&#xff09; 必要条件&#xff1a;有源元件&#xff08;能量控制原件&#xff09; 前提&#xff1a;不失真 测试的…

详解接口的常见请求方式

详解接口的常见请求方式 一、 常见接口请求方式1. GET2. POST3. PUT4. DELETE5. PATCH6. HEAD7. OPTIONS 二、 实现方法1. 前端实现2. 后端实现 三、 作用与主要区别四、 举例讲解1. 创建 Spring Boot 工程2. 添加依赖3. 编写 Controller 实现接口关键点说明 4. 启动与测试5. 总…

【附代码】【MILP建模】3D装箱问题(3D-Bin Packing Problem)

文章目录 相关教程相关文献问题描述建模思路——carton 方向平行轴建模方法&#xff08;9变量6约束&#xff09;平行轴建模方法&#xff08;4变量8约束&#xff09;枚举建模方法&#xff08;6变量1约束&#xff09; 建模思路——carton 位置平行轴建模方法枚举建模方法 Bin长宽…

【计算机网络中的奈氏准则与香农定理】

文章目录 一、前言二、奈氏准则1. 概念2. 奈氏准则公式3. 奈氏准则的意义 三、香农定理1. 概念2. 香农定理公式3. 香农定理的意义 四、奈氏准则与香农定理的对比五、应用示例1. 奈氏准则示例2. 香农定理示例 六、总结 一、前言 在计算机网络中&#xff0c;数据的传输速率与信道…

【C++】回调函数和回调对象

文章目录 回调可调用对象函数指针作回调函数对象作回调函数对象的使用std::function【C11】作回调使用 【C11】Lambda表达式作回调【C11】bind对象作回调std::bind的使用作回调使用 回调 当发生某种事件时需要调用或触发另一个事件即为回调&#xff0c;回调的核心即为将可调用…

DeepSeek助力文案,智能音箱如何改变你的生活?

你好&#xff0c;我是三桥君 你有没有为写智能音箱的宣传文案而抓耳挠腮过&#xff1f;三桥君在这方面可是有些感想&#xff0c;今天就来给你唠唠怎么用DeepSeek写出超赞的智能音箱宣传文案。 首先&#xff0c;你得给DeepSeek喂足“料”。这就好比做饭&#xff0c;你得准备好各…

【区块链安全 | 第一篇】密码学原理

文章目录 1.哈希函数1.1 哈希函数的性质1.2 常见哈希算法1.3 Merkle Tree&#xff08;默克尔树&#xff09;1.4 HMAC&#xff08;哈希消息认证码&#xff09; 2. 公钥密码学2.1 对称加密 vs 非对称加密2.2 RSA 算法2.3 ECC&#xff08;椭圆曲线密码学&#xff09;2.4 Diffie-He…

基于websocketpp实现的五子棋项目

该博客对于学完C和linux操作系统&#xff0c;但不知道如何用C开发项目&#xff0c;已经不知道C如何使用第三方库的人来说一定很有帮助&#xff0c;请耐心看完&#xff01; 先看一下游戏会显示的前端界面&#xff0c;对理解这个游戏的前后端交互过程会有帮助 1. 开发环境 1.1 …