问题描述
小包非常喜欢吃甜点,他收到了一次性送来的 NN 个甜点,每个甜点都有一个对应的喜爱值。
但是这还不够!小包让小哥连续送来了 MM 次相同的 NN 个甜点,并将这些甜点首尾相接排成一排。
现在,小包面前有 (N×M)(N×M) 个排成一排的甜点,小包希望从中选择一段连续的甜点,使得这段甜点的总喜爱值最大化。
注意:尽管小包喜欢甜食,但有些甜点可能不合口味,导致其喜爱值为负数。小包至少要选择一个甜点来满足他对甜点的贪心。
输入参数
- 整数 ( N ):表示每次送来的甜点数量。
- 整数 ( M ):表示送来的次数。
- 数组
data:长度为 ( N ),表示每个甜点的喜爱值。
返回结果
- 一个整数,表示在 N×MN×M 个甜点中可以选择的连续甜点段的最大总喜爱值。
题目解析:最大子数组和的变形问题
题目理解
小包希望在连续N * M个甜点中选择一段连续的甜点,使得这一段的总喜爱值最大。这里需要解决的核心问题是:在一个“循环数组”中寻找最大子数组和。
难点解析
- 重复数据处理:甜点数组的重复M次扩展形成了一个长度为N * M的数组。如何处理这种扩展是解决问题的关键。
- 动态规划优化:直接扩展数组会导致时间复杂度提升,而使用动态规划方法能够大幅优化性能。
代码解析:思路与实现
为了解决这个问题,我采用了以下策略:
1. 数据扩展
我们通过循环对数据进行扩展,将原始数组ata重复M次生成长度为N * M的新数组wight。
2. 动态规划求解
动态规划的核心是计算以每个位置结尾的连续子数组的最大和,具体公式如下:
该公式表示当前元素是否与前面的子数组相连,以求得最大值。
3. 最大值计算
在动态规划数组 ( dp ) 中,我们遍历寻找最大值即为结果。
代码实现
以下是实现代码的核心部分:
public static int solution(int N, int M, int[] data) {
int[] wight = new int[N * M];
for (int i = 0; i < N * M; i++) {
wight[i] = data[i % N]; // 扩展数组
}
int[] dp = new int[N * M];
for (int i = 0; i < dp.length; i++) {
if (i == 0) {
dp[i] = wight[i];
} else if (dp[i - 1] <= 0) {
dp[i] = wight[i];
} else {
dp[i] = dp[i - 1] + wight[i];
}
}
int max = dp[0];
for (int j = 1; j < dp.length; j++) {
max = Math.max(max, dp[j]); // 找到最大值
}
return max;
}
知识总结:动态规划与循环数组的结合
知识点 1:动态规划解最大子数组和问题
动态规划方法在解决连续子数组和问题时效率极高,其核心在于只需要保留前一个状态即可,有效减少冗余计算。
知识点 2:循环数组的处理
通过对数组的重复扩展,形成逻辑上的“循环数组”。在处理此类问题时,可以通过取模操作减少实际存储的开销。
建议
对于入门学习者,在学习动态规划时,建议从经典问题(如最大子数组和)开始,逐步理解状态转移方程的设计和优化技巧。
学习计划:高效刷题策略
-
逐步深入,掌握基础问题
动态规划问题初期可选择简单的场景,比如:- 经典最大子数组和
- 爬楼梯问题
- 背包问题 随着掌握程度逐步过渡到涉及多重状态的复杂问题。
-
利用错题库进行复盘
在豆包MarsCode AI平台刷题过程中,记录每道错题的原因,分析理解不足的地方,并通过相似题目练习加深记忆。 -
分阶段制定目标
根据题目难度分类,每阶段集中攻克某一类型(如数组、图算法),然后进行总结和归纳。
工具运用:结合多种资源提升效率
在使用豆包MarsCode AI平台时,我将其刷题功能与以下资源结合起来,达到了良好的学习效果:
- 算法书籍:结合《算法导论》《剑指Offer》等经典书籍理解理论知识。
- 视频学习:通过观看动态规划和算法设计的视频教程,补充理论盲点。
- 社区交流:在平台讨论区中分享心得,获取不同的解题思路。
结语
通过这道甜点最大喜爱值的问题,我不仅复习了动态规划的核心思想,还深入理解了如何处理循环数组。在豆包MarsCode AI平台的帮助下,我的刷题效率显著提高。希望大家在使用这类工具时,多关注解题思路的分析,逐步提升自己的算法能力。