题目

洛谷:P1115 最大子段和

LeetCode:最大子序和

给出一个长度为 \(n\) 的序列 \(a\),选出其中连续且非空的一段使得这段和最大。

挺经典的一道题目,下面分别介绍 \(O(n)\) 的 DP 做法、前缀和做法,以及 \(O(n\log n)\) 的分治做法。

DP 做法

用 \(d_i\) 表示结尾为位置 \(i\) 的最大区间和,则有

\[d_i=\max(d_{i-1},0)+a_i
\]

问题的答案即为 \(\max\{d_i \mid i\in[1,n]\}\)。

编写代码时不需要开 \(d\) 数组,用变量 last_d 记录 \(d_{i-1}\),变量 ans 记录 \(\max\{d_i\}\),并在扫描时动态更新即可。

时间复杂度 \(O(n)\),空间复杂度 \(O(1)\)。

核心代码如下:

maxn = int(2e5 + 5)
arr = [0 for _ in range(maxn)] # 从下标 1 开始存 # 输入过程略…… ans = None
last_d = 0
for i in range(1, n + 1):
temp_ans = max(last_d, 0) + arr[i]
if ans is None or temp_ans > ans:
ans = temp_ans
last_d = temp_ans
print(ans)

前缀和做法

将数列前 \(n\) 项的和记为 \(sum_n\):

\[sum_n=\sum_{i=1}^n a_i
\]

可以用前缀和快速求区间和:

\[\sum_{i=x}^y a_i = sum_y - sum_{x-1}
\]

用 \(d_i\) 表示结尾为位置 \(i\) 的最大区间和,则有

\[d_i=sum_i-\min\{sum_j \mid j<i\}
\]

问题的答案即为 \(\max\{d_i \mid i \in [1,n]\}\)。

编写代码时只需要开前缀和数组,无需开 \(d\) 数组,用变量 cur_min_pre_sum 记录 \(\min\{sum_j\}\),变量 ans 记录 \(\max\{d_i\}\),并动态维护即可。

时间复杂度 \(O(n)\),空间复杂度 \(O(n)\)。

核心代码如下:

maxn = int(2e5 + 5)
arr = [0 for _ in range(maxn)] # 原数组,从下标 1 开始存
pre_sum = [0 for _ in range(maxn)] # 前缀和数组 # 输入过程略…… # 预处理前缀和
for i in range(1, n + 1):
pre_sum[i] = pre_sum[i - 1] + arr[i] cur_min_pre_sum = 0
ans = None
for i in range(1, n + 1):
temp_ans = pre_sum[i] - cur_min_pre_sum
if ans is None or temp_ans > ans:
ans = temp_ans
cur_min_pre_sum = min(cur_min_pre_sum, pre_sum[i])
print(ans)

分治做法

若有一区间 \([start,stop)\),区间中点为 \(mid\),其最大子段和对应的子区间为 \([i,j)\),则 \([i,j)\) 只有以下三种情况:

  1. \([i,j)\) 完全在左子区间 \([start,mid)\) 内;
  2. \([i,j)\) 完全在右子区间 \([mid,stop)\) 内;
  3. \([i,j)\) 横跨中点 \(mid\)。

求出这三种情况下的值,取最大的即可。

前两种情况可通过递归求解,求解第三种情况需要一点技巧,方法是从中点出发分别向左右两边延伸

时间复杂度 \(O(n\log n)\)。

核心代码如下:

maxn = int(2e5 + 5)
arr = [0 for _ in range(maxn)] # 从下标 1 开始存 # 从位置 mid - 1 开始向左延伸的最大区间和
# 注:左子区间 [start, mid)
def mid_lmax(start: int, mid: int) -> int:
ans = None
cur_sum = 0
for i in range(mid - 1, start - 1, -1):
cur_sum += arr[i]
if ans is None or cur_sum > ans:
ans = cur_sum
return ans # 从位置 mid 开始向右延伸的最大区间和
# 注:右子区间 [mid, stop)
def mid_rmax(mid: int, stop: int) -> int:
ans = None
cur_sum = 0
for i in range(mid, stop):
cur_sum += arr[i]
if ans is None or cur_sum > ans:
ans = cur_sum
return ans # [start, stop) 的最大子区间和
def solve(start: int, stop: int) -> int:
if stop - start == 1:
return arr[start]
mid = (start + stop) // 2
only_lmax = solve(start, mid) # 完全在左子区间内
only_rmax = solve(mid, stop) # 完全在右子区间内
span_max = mid_lmax(start, mid) + mid_rmax(mid, stop) # 横跨中点
return max(only_lmax, only_rmax, span_max)

三种方法求解最大子区间和:DP、前缀和、分治的更多相关文章

  1. 使用三种方法求解前N个正整数的排列

    本篇博文给大家介绍前N个正整数的排列求解的三种方式.第一种是暴力求解法:第二种则另外声明了一个长度为N的数组,并且将已经排列过的数字保存其中:第三种方式则采用了另外一种思路,即首先获取N个整数的升序排 ...

  2. Java/JSP获得客户端网卡MAC地址的三种方法解析

    java/jsp获得客户端(IE)网卡MAC地址的方法大概有三种. 1.通过命令方式,在客户端执行Ipconfig 等等.(java/jsp) 2.通过ActiveX的方法.(jsp) 3.通过向13 ...

  3. 数组k平移三种方法(java)

    上代码,本文用了三种方法实现,时间复杂度不一样,空间复杂度都是o(1): public class ArrayKMove { /** * 问题:数组的向左k平移,k小于数组长度 * @param ar ...

  4. C#中??和?分别是什么意思? 在ASP.NET开发中一些单词的标准缩写 C#SESSION丢失问题的解决办法 在C#中INTERFACE与ABSTRACT CLASS的区别 SQL命令语句小技巧 JQUERY判断CHECKBOX是否选中三种方法 JS中!=、==、!==、===的用法和区别 在对象比较中,对象相等和对象一致分别指的是什么?

    C#中??和?分别是什么意思? 在C#中??和?分别是什么意思? 1. 可空类型修饰符(?):引用类型可以使用空引用表示一个不存在的值,而值类型通常不能表示为空.例如:string str=null; ...

  5. 像画笔一样慢慢画出Path的三种方法(补充第四种)

    今天大家在群里大家非常热闹的讨论像画笔一样慢慢画出Path的这种效果该如何实现. 北京-LGL 博客号@ligl007发起了这个话题.然后各路高手踊跃发表意见.最后雷叔 上海-雷蒙 博客号@雷蒙之星 ...

  6. JAVA之线程同步的三种方法

    最近接触到一个图片加载的项目,其中有声明到的线程池等资源需要在系统中线程共享,所以就去研究了一下线程同步的知识,总结了三种常用的线程同步的方法,特来与大家分享一下.这三种方法分别是:synchroni ...

  7. java解析xml的三种方法

    java解析XML的三种方法 1.SAX事件解析 package com.wzh.sax; import org.xml.sax.Attributes; import org.xml.sax.SAXE ...

  8. 【Android】Eclipse自动编译NDK/JNI的三种方法

    [Android]Eclipse自动编译NDK/JNI的三种方法 SkySeraph Sep. 18th  2014 Email:skyseraph00@163.com 更多精彩请直接访问SkySer ...

  9. DataTable数据批量写入数据库三种方法比较

    DataTable数据批量写入数据库三种方法比较 标签: it 分类: C#1)   insert循环插入:2)   sqldataadapter.update(dataset,tablename); ...

随机推荐

  1. MySQL基础——常用命令

    一.连接MySQL 1.启动mysql首先在打开cmd窗口,输入mysql -uroot -p ,然后空格进入MySQL控制台,MySQL的提示符是: mysql>. mysql -uroot ...

  2. MySQL锁(表锁,行锁,共享锁,排它锁,间隙锁)使用详解

    锁,在现实生活中是为我们想要隐藏于外界所使用的一种工具.在计算机中,是协调多个进程或县城并发访问某一资源的一种机制.在数据库当中,除了传统的计算资源(CPU.RAM.I/O等等)的争用之外,数据也是一 ...

  3. Linux系统的ssh与sshd服务

    当主机中开启openssh服务,那么就对外开放了远程连接的接口 ssh为openssh服务的客户端,sshd为openssh服务的服务端 远程管理工具ssh具有数据加密传输.网络开销小以及应用平台范围 ...

  4. sublime text build system automatic ctrl/cmd+B自动选择 python2 或 python3

    背景 我同时安装了 python2 和 python3 时,python 指向 python2,python3 才是 python3 默认情况下,在 Sublime 内 Ctrl/Cmd + B 运行 ...

  5. SQL-Instead of 触发器

    定义及优点 INSTEAD OF触发器指定执行触发器而不是执行触发 的SQL 语句,从而替代触发语句的操作.        在表或视图上,每个 INSERT.UPDATE 或 DELETE 语句最多可 ...

  6. AspectJ——AOP框架快速入门

    一.导包 二.bean.xml配置 三.环绕通知 四,表达式

  7. 机器学习——最优化问题:拉格朗日乘子法、KKT条件以及对偶问题

    1 前言 拉格朗日乘子法(Lagrange Multiplier)  和 KKT(Karush-Kuhn-Tucker)  条件是求解约束优化问题的重要方法,在有等式约束时使用拉格朗日乘子法,在有不等 ...

  8. RE Validation in JavaEE

    以下是关于RE校验的一些方法的基本功能: import java.util.regex.Pattern;<br> /** * 校验器:利用正则表达式校验邮箱.手机号等 * */<br ...

  9. PHP的内置WEB服务器

    在很多时候,我们需要简单的运行一个小 demo 来验证一些代码或者轮子是否可用,是否可以运行起来,但是去配 nginx 或者 apache 都很麻烦,其实,PHP CLI 已经提供了一个简单的测试服务 ...

  10. html2canvas实现截取指定区域或iframe的区域

    官网文档: http://html2canvas.hertzen.com/ 使用的是 jquery 3.2.1   html2canvas 1.0.0-rc.7 截取根据id的指定区域: var ca ...