news 2026/10/29 21:33:48

LeetCode 1388:环形数组动态规划解3n披萨问题

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LeetCode 1388:环形数组动态规划解3n披萨问题

1. 问题背景与题目解析

LeetCode 1388题"3n块披萨"是一个经典的动态规划问题,题目描述如下:你有一块披萨,上面有3n块切片,每块切片的大小不同。你需要选择n块切片,但选择的规则是:不能选择相邻的两块切片(即如果你选择了第i块切片,就不能选择第i-1和i+1块切片)。目标是最大化所选n块切片的大小总和。

这道题看似简单,但实际上考察了对环形数组和动态规划的综合应用能力。题目给出的函数签名是:

public int maxSizeSlices(int[] slices)

其中slices数组表示披萨上各切片的大小,我们需要返回能选择的最大总和。这个问题可以转化为:在一个环形数组中,选择不相邻的n个元素,使它们的和最大。

2. 解题思路分析

2.1 问题转化

首先,我们需要认识到这个问题与经典的"打家劫舍"问题类似,但有两个关键区别:

  1. 数组是环形的(首尾相连)
  2. 需要选择恰好n个元素,而不是尽可能多的元素

对于环形数组问题,常见的处理方法是将其转化为两个线性问题:

  1. 不考虑第一个元素(即只能在第二个到最后一个元素中选择)
  2. 不考虑最后一个元素(即只能在第一个到倒数第二个元素中选择)

然后分别求解这两个线性问题,取较大的结果作为最终答案。

2.2 动态规划状态定义

对于线性数组的情况,我们可以定义dp[i][j]表示在前i个元素中选择了j个元素的最大和。状态转移方程为:

dp[i][j] = max( dp[i-1][j], // 不选第i个元素 dp[i-2][j-1] + slices[i] // 选第i个元素 )

其中边界条件是:

  • dp[0][j] = -∞ (j > 0),因为无法从0个元素中选择j>0个元素
  • dp[i][0] = 0,因为选择0个元素的和为0

2.3 环形数组处理

对于环形数组,我们需要分别计算两种情况:

  1. 不选第一个元素,在slices[1...n-1]中选择n个元素
  2. 不选最后一个元素,在slices[0...n-2]中选择n个元素

最终结果是这两种情况的最大值。

3. 代码实现与优化

3.1 基础实现

基于上述思路,我们可以实现如下Java代码:

public int maxSizeSlices(int[] slices) { int n = slices.length; int k = n / 3; // 需要选择k=n/3个元素 // 情况1:不选第一个元素 int[][] dp1 = new int[n][k+1]; for (int i = 1; i < n; i++) { for (int j = 1; j <= k; j++) { if (i == 1) { dp1[i][j] = Math.max(slices[i], dp1[i-1][j]); } else { dp1[i][j] = Math.max(dp1[i-1][j], dp1[i-2][j-1] + slices[i]); } } } // 情况2:不选最后一个元素 int[][] dp2 = new int[n][k+1]; for (int i = 0; i < n-1; i++) { for (int j = 1; j <= k; j++) { if (i == 0) { dp2[i][j] = slices[i]; } else if (i == 1) { dp2[i][j] = Math.max(slices[i], dp2[i-1][j]); } else { dp2[i][j] = Math.max(dp2[i-1][j], dp2[i-2][j-1] + slices[i]); } } } return Math.max(dp1[n-1][k], dp2[n-2][k]); }

3.2 空间优化

注意到dp[i][j]只依赖于dp[i-1][j]和dp[i-2][j-1],我们可以将空间复杂度从O(nk)优化到O(k):

public int maxSizeSlices(int[] slices) { int n = slices.length; int k = n / 3; // 情况1:不选第一个元素 int[] dp1_prev = new int[k+1]; int[] dp1_curr = new int[k+1]; int[] dp1_next = new int[k+1]; for (int i = 1; i < n; i++) { for (int j = 1; j <= k; j++) { dp1_next[j] = Math.max(dp1_curr[j], dp1_prev[j-1] + slices[i]); } dp1_prev = dp1_curr; dp1_curr = dp1_next; dp1_next = new int[k+1]; } // 情况2:不选最后一个元素 int[] dp2_prev = new int[k+1]; int[] dp2_curr = new int[k+1]; int[] dp2_next = new int[k+1]; for (int i = 0; i < n-1; i++) { for (int j = 1; j <= k; j++) { if (i == 0) { dp2_next[j] = slices[i]; } else { dp2_next[j] = Math.max(dp2_curr[j], dp2_prev[j-1] + slices[i]); } } dp2_prev = dp2_curr; dp2_curr = dp2_next; dp2_next = new int[k+1]; } return Math.max(dp1_curr[k], dp2_curr[k]); }

4. 复杂度分析与边界条件

4.1 时间复杂度

算法的时间复杂度主要取决于两层循环:

  • 外层循环遍历数组,O(n)
  • 内层循环遍历选择的元素数量,O(k)
  • 总时间复杂度为O(nk) = O(n^2),因为k = n/3

4.2 空间复杂度

经过空间优化后,我们只需要维护三个长度为k+1的数组,因此空间复杂度为O(k) = O(n)

4.3 边界条件处理

需要特别注意以下边界条件:

  1. 当n=1时,只能选择这唯一的一块切片
  2. 当n=3时,必须选择其中一块切片(因为不能选相邻的两块)
  3. 当所有切片大小相同时,任意选择n个不相邻的切片即可

5. 测试用例与验证

为了验证代码的正确性,我们可以设计以下几组测试用例:

// 测试用例1:简单情况 int[] slices1 = {1,2,3,4,5,6}; assert maxSizeSlices(slices1) == 10; // 选择2,4,6 // 测试用例2:环形情况 int[] slices2 = {8,9,8,6,1,1}; assert maxSizeSlices(slices2) == 16; // 选择第一个和第三个8 // 测试用例3:所有元素相同 int[] slices3 = {3,3,3,3,3,3}; assert maxSizeSlices(slices3) == 9; // 选择任意三个不相邻的3 // 测试用例4:边界情况n=1 int[] slices4 = {5}; assert maxSizeSlices(slices4) == 5; // 测试用例5:边界情况n=3 int[] slices5 = {1,2,3}; assert maxSizeSlices(slices5) == 3; // 只能选择中间那个

6. 实际应用与扩展

6.1 实际问题中的应用

这类问题在实际中有很多应用场景,例如:

  1. 资源分配问题:在有限的资源中选择最优组合,且某些资源不能同时选择
  2. 任务调度:选择不相邻的时间段执行任务以获得最大收益
  3. 网络路由:选择不相邻的节点进行数据传输

6.2 算法扩展

基于这个问题的解法,我们可以进一步思考:

  1. 如果披萨不是环形的,而是线性的,如何简化解法?
  2. 如果可以选择任意数量的切片(不一定是n块),但总和最大且不相邻,如何解决?
  3. 如果切片的选择限制更复杂(比如不能选择相邻的三个切片),如何修改算法?

6.3 性能优化

对于大规模数据,可以考虑以下优化:

  1. 使用记忆化搜索替代动态规划,减少不必要的计算
  2. 并行计算两种情况的动态规划
  3. 使用更高效的数据结构存储中间结果

7. 常见错误与调试技巧

在解决这个问题时,容易犯以下错误:

  1. 忘记处理环形数组的情况,直接当作线性数组处理
  2. 动态规划状态转移方程写错,特别是边界条件处理不当
  3. 空间优化时混淆了数组的更新顺序

调试技巧:

  1. 打印中间dp表的值,验证是否符合预期
  2. 从小规模测试用例开始,逐步增加复杂度
  3. 对比线性情况和环形情况的结果,检查差异是否合理

8. 与其他LeetCode题目的联系

这个问题与以下几个LeetCode题目有相似之处:

    1. 打家劫舍:线性数组中选择不相邻元素的最大和
    1. 打家劫舍II:环形数组中选择不相邻元素的最大和
    1. 打家劫舍III:树形结构中选择不相邻节点的最大和

理解这些题目的共性和差异,有助于形成系统的动态规划解题思路。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/29 21:32:49

Win32对话框机制全解:模板、通用对话框与钩子避坑

简介&#xff1a;2020版MSDN文档PDF聚焦Windows API中的对话框开发主题&#xff0c;面向使用C/C进行Win32桌面应用开发的初中级程序员&#xff0c;以及需要查阅微软官方接口说明的开发者&#xff0c;可用于系统梳理通用对话框、自定义对话框与消息处理等知识。压缩包内为1个PDF…

作者头像 李华
网站建设 2026/10/29 21:31:51

YOLOv8在医疗影像诊断中的应用与优化实践

1. 项目概述&#xff1a;当YOLOv8遇上医疗影像诊断去年参与某三甲医院PACS系统升级时&#xff0c;我亲眼见证了放射科医生每天需要审阅超过200张胸部X光片的超负荷工作状态。传统人工阅片方式对早期肺炎征象的漏诊率高达15-20%&#xff0c;这正是我们团队决定开发这个智能辅助诊…

作者头像 李华
网站建设 2026/10/29 21:31:49

iVMS-8700客户端安装与电视墙配置全攻略:版本对齐与避坑指南

简介&#xff1a;这份资源是海康威视iVMS-8700客户端的操作说明文档&#xff0c;面向智能楼宇视频监控系统的实施人员、运维工程师及需要上手该平台的初、中级用户&#xff0c;重点解决电视墙操作平台中录像机添加与录像键盘配置等实际配置问题。压缩包内共1个docx文件&#xf…

作者头像 李华
网站建设 2026/10/29 21:30:46

STM32 GPS定位终端开发:串口DMA与NMEA解析实战

做嵌入式这行,凡是把STM32微控制器和GPS导航系统放在一起的项目,十有八九都会经历同一个心路历程:第一天觉得"不就是串口读字符串嘛",第七天开始怀疑人生,第十四天对着屏幕上乱跳的经纬度发呆。我前后做过几个类似的定位终端,有车载轨迹记录的,有手持测绘打点的,也有…

作者头像 李华
网站建设 2026/10/29 21:30:43

YOLOv26目标检测架构优化:可学习缩放因子与梯度流优化

1. 项目概述&#xff1a;YOLOv26架构的瓶颈与突破方向 目标检测领域近年来最引人注目的进展莫过于YOLO系列模型的持续进化。作为该系列的最新成员&#xff0c;YOLOv26在保持实时性优势的同时&#xff0c;其精度表现已经逼近两阶段检测器。但在实际工业部署中&#xff0c;我们发…

作者头像 李华
网站建设 2026/10/29 21:29:45

STM32+GNSS GPS导航系统:NMEA解析与偏航判断实践

做带定位功能的小板子&#xff0c;绕不开STM32 微控制器 GNSS 模块这套组合。它便宜、资料多、串口一接就能吐坐标&#xff0c;看起来门槛极低&#xff1b;但真把它做成一台能用的GPS 导航系统&#xff0c;从硬件布板到报文解析、从坐标换算到航线偏航判断&#xff0c;中间的坑…

作者头像 李华