讲师中心 微信公众号
AI工具推荐 视频效率加速

如何高效分割数组以最大化各子数组极差之和

老辰小哥_6659

老辰小哥_6659

发布时间:2026-07-09 16:36:50

|

1003人浏览过

|

来源于php中文网

原创

本文介绍一种基于动态规划与滑动窗口优化的高效算法,用于将整数数组按长度约束划分为连续子数组,使得所有子数组(最大值−最小值)之和达到最大。时间复杂度为 o((b−a+1)·n),显著优于暴力回溯。

本文介绍一种基于动态规划与滑动窗口优化的高效算法,用于将整数数组按长度约束划分为连续子数组,使得所有子数组(最大值−最小值)之和达到最大。时间复杂度为 o((b−a+1)·n),显著优于暴力回溯。

在解决“将数组划分为长度介于 a 到 b 之间的连续子数组,使各子数组极差(max − min)之和最大”这一问题时,朴素的回溯或 BFS 方法(如原始代码中使用的队列枚举)时间复杂度高达指数级,无法应对中等规模输入(例如 n > 30)。幸运的是,该问题具备最优子结构重叠子问题特性,天然适配动态规划(DP),并可通过单调双端队列优化区间极值计算,实现线性单次扫描。

核心思路:DP 状态 + 滑动窗口预处理

我们定义 DP 状态 best_weight[i] 表示处理完前 i 个元素(即 numbers[0:i])所能获得的最大极差总和;对应地,prev_index[i] 记录达成该最优值时,上一个子数组的起始下标(便于最终重构划分方案)。

关键挑战在于:对每个可能的结束位置 j,需快速计算所有满足 j−b+1 ≤ i ≤ j−a+1 的起始位置 i 对应的子数组 numbers[i:j+1] 的极差。若对每个子数组都调用 max()/min(),单次耗时 O(b−a),整体退化为 O(n·(b−a)²)。

✅ 解决方案:复用滑动窗口极值算法。我们不逐个枚举长度,而是对每个固定长度 L = a 启动一次单调双端队列扫描,同时在扩展过程中动态维护当前窗口 [i, j](j−i+1 ∈ [a, b])的 min/max,并即时更新 DP 状态。

以下为完整实现(含注释):

from collections import deque

def window_mins_maxes(size, array):
    """O(n) 单次扫描,返回所有长度为 size 的窗口的 (end_idx, min_val, max_val)"""
    if size == 0 or not array:
        return
    min_vals, min_pos = deque(), deque()
    max_vals, max_pos = deque(), deque()

    for i, val in enumerate(array):
        # 移除过期索引(窗口左边界超出)
        if i >= size:
            if min_pos and min_pos[0] <= i - size:
                min_vals.popleft()
                min_pos.popleft()
            if max_pos and max_pos[0] <= i - size:
                max_vals.popleft()
                max_pos.popleft()

        # 维护 min_vals 单调递增(队首最小)
        while min_vals and val <= min_vals[-1]:
            min_vals.pop()
            min_pos.pop()
        min_vals.append(val)
        min_pos.append(i)

        # 维护 max_vals 单调递减(队首最大)
        while max_vals and max_vals[-1] <= val:
            max_vals.pop()
            max_pos.pop()
        max_vals.append(val)
        max_pos.append(i)

        # 当窗口填满 size 个元素时输出
        if i >= size - 1:
            yield (i, min_vals[0], max_vals[0])

def partition_array(numbers, min_len, max_len):
    n = len(numbers)
    if max_len < min_len or n < min_len:
        return (None, None)

    # best_weight[i] = 前 i 个元素的最大极差和;索引 0..n,额外预留 best_weight[n] 表示全数组处理完毕
    best_weight = [None] * (n + 1)
    prev_index = [None] * (n + 1)
    best_weight[0] = 0  # 空前缀和为 0

    # 主循环:对每个以 i 结尾、长度为 min_len 的窗口,向后延伸至 max_len
    for end, win_min, win_max in window_mins_maxes(min_len, numbers):
        start = end - min_len + 1
        base_weight = best_weight[start]  # 上一状态:numbers[0:start] 的最优解
        if base_weight is None:
            continue

        # 从长度 min_len 开始,逐步扩展子数组至长度 max_len
        curr_min, curr_max = win_min, win_max
        # 当前窗口 [start, end] 已确定,尝试向右扩展:end+1, end+2, ..., 最多到 start + max_len - 1
        for j in range(end + 1, min(start + max_len, n + 1)):
            # 更新 [start, j-1] 的极差(j 是新结束索引,对应子数组 numbers[start:j])
            if j - 1 < n:  # j-1 是实际最后一个元素下标
                if numbers[j - 1] < curr_min:
                    curr_min = numbers[j - 1]
                if numbers[j - 1] > curr_max:
                    curr_max = numbers[j - 1]
            diff = curr_max - curr_min
            new_weight = base_weight + diff

            # 更新 DP 状态:若更优,则记录
            if best_weight[j] is None or best_weight[j] < new_weight:
                best_weight[j] = new_weight
                prev_index[j] = start

    # 检查是否能覆盖整个数组
    if best_weight[n] is None:
        return (None, None)

    # 回溯构造划分方案
    path = [n]
    while prev_index[path[-1]] is not None:
        path.append(prev_index[path[-1]])
    path = list(reversed(path))
    partitioned = [numbers[path[i]:path[i + 1]] for i in range(len(path) - 1)]
    return (best_weight[n], partitioned)

# 测试用例验证
print(partition_array([5, 8, 4, 5, 1, 3, 5, 1, 3, 1], 3, 7))
# → (12, [[5, 8, 4], [5, 1, 3], [5, 1, 3, 1]])

print(partition_array([1, 6, 2, 2, 5, 2, 8, 1, 5, 6], 3, 4))
# → (16, [[1, 6, 2], [2, 5, 2, 8], [1, 5, 6]])

print(partition_array([5, 8, 4, 5, 1, 3, 5, 1, 3, 1, 2], 4, 5))
# → (None, None) —— 无法划分

注意事项与优化要点

  • 时间复杂度:主循环调用 window_mins_maxes(min_len, ...) 为 O(n),内部扩展最多 (max_len − min_len) 步,故总时间为 O(n·(max_len − min_len + 1)),远优于暴力的 O(b^ⁿ)。
  • 空间复杂度:仅需 O(n) 存储 DP 数组及双端队列(队列长度 ≤ max_len),符合线性要求。
  • 边界鲁棒性:代码显式处理了 min_len > max_len、数组过短、无法完全划分等异常情况,返回 (None, None) 明确标识失败。
  • 重构路径:利用 prev_index 数组反向追踪,可在 O(k) 时间内(k 为子数组数量)还原具体划分,无需额外存储中间状态。
  • 不可贪心:该问题不能使用贪心策略(如每次取最长/极差最大子数组),因局部最优不保证全局最优。DP 是理论最优且实践高效的解法。

综上,该方案将经典 DP 框架与滑动窗口技巧深度融合,在保证正确性的同时实现了接近理论下限的运行效率,是处理此类带约束区间划分优化问题的标准范式。

热门AI工具

更多
蛙蛙写作

一款AI论文写作工具,主要用于超级AI智能写作助手,适合需要提升相关任务效率的用户。

SkildArt
SkildArt Hot

SkildArt是一款AI文本写作工具,一站式 AI 视觉创作平台。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

Laper
Laper Hot

Laper是专为编剧、导演和制片人推出的 AI 原生剧本创作工具。

切问学术

切问学术是一款AI论文写作工具,复旦大学NLP团队推出的AI学术智能体。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

UpDream
UpDream Hot

一款AI视频创作工具,主要用于哔哩哔哩推出的自研AI视频创作工具,适合需要提升相关任务效率的用户。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

Seko
Seko Hot

一款AI视频创作工具,主要用于商汤科技推出的创编一体的AI短视频创作Agent,适合需要提升相关任务效率的用户。

相关专题

更多
python打包成可执行文件
python打包成可执行文件

本专题为大家带来python打包成可执行文件相关的文章,大家可以免费的下载体验。

1571

2023.07.20

python能做什么
python能做什么

python能做的有:可用于开发基于控制台的应用程序、多媒体部分开发、用于开发基于Web的应用程序、使用python处理数据、系统编程等等。本专题为大家提供python相关的各种文章、以及下载和课程。

3724

2023.07.25

format在python中的用法
format在python中的用法

Python中的format是一种字符串格式化方法,用于将变量或值插入到字符串中的占位符位置。通过format方法,我们可以动态地构建字符串,使其包含不同值。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

1569

2023.07.31

python教程
python教程

Python已成为一门网红语言,即使是在非编程开发者当中,也掀起了一股学习的热潮。本专题为大家带来python教程的相关文章,大家可以免费体验学习。

21317

2023.08.03

python环境变量的配置
python环境变量的配置

Python是一种流行的编程语言,被广泛用于软件开发、数据分析和科学计算等领域。在安装Python之后,我们需要配置环境变量,以便在任何位置都能够访问Python的可执行文件。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2627

2023.08.04

python eval
python eval

eval函数是Python中一个非常强大的函数,它可以将字符串作为Python代码进行执行,实现动态编程的效果。然而,由于其潜在的安全风险和性能问题,需要谨慎使用。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2687

2023.08.04

scratch和python区别
scratch和python区别

scratch和python的区别:1、scratch是一种专为初学者设计的图形化编程语言,python是一种文本编程语言;2、scratch使用的是基于积木的编程语法,python采用更加传统的文本编程语法等等。本专题为大家提供scratch和python相关的文章、下载、课程内容,供大家免费下载体验。

1083

2023.08.11

python合并两个列表
python合并两个列表

Python是一种强大的编程语言,具有许多方便的功能和工具。在Python中,有多种方法可以合并两个列表。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

576

2023.08.10

Buffalo框架数据库开发全教程
Buffalo框架数据库开发全教程

本专题围绕Buffalo框架数据库开发,讲解database.yml多环境配置、soda与fizz迁移生成回滚、模型结构体标签、增删改查与条件查询、一对多与多对多关联、数据校验、回调钩子、事务处理及原生SQL执行能力。

20

2026.09.23

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn