数组反转拆分生成最大和数组:代码逻辑错误排查与修正
问题分析与代码修正
嘿,我发现你的代码核心问题在于贪心策略的短视性——每次只选当前看起来最优的操作,但这种局部最优的选择往往会错过全局最大总和。比如你给的示例A=[1,4,2,3,5],贪心可能会先选拿最后一个元素5,后续的选择总和肯定达不到24,因为局部最优不等于全局最优。咱们换个思路,用动态规划来解决这个问题,就能覆盖所有情况啦!
为什么贪心不行?
贪心算法只看当下的最大收益,忽略了后续操作的潜在收益。比如某些情况下,当下选一个小的元素,却能让后续拿到更大的乘积总和,贪心会直接错过这种情况。
正确思路:动态规划(DP)
我们可以用动态规划记录每个子区间的最大收益,避免重复计算。具体定义:
dp[i][j]:表示数组从第i个元素到第j个元素(闭区间),能获得的最大总和。
然后针对四种操作,推导状态转移:
- 操作1(取最后一个元素):总和 = 子区间
i到j-1的最大和 +A[j] - 操作2(取最后两个元素乘积):总和 = 子区间
i到j-2的最大和 +A[j]*A[j-1](仅当区间长度≥2时有效) - 操作3(反转后取最后一个):等价于取第一个元素,总和 = 子区间
i+1到j的最大和 +A[i] - 操作4(反转后取最后两个乘积):等价于取前两个元素乘积,总和 = 子区间
i+2到j的最大和 +A[i]*A[i+1](仅当区间长度≥2时有效)
我们按区间长度从小到大递推,先算短区间的最优解,再用这些结果推导长区间的最优解,最终整个数组的最优解就是dp[0][n-1]。
修正后的Java代码
import java.util.Scanner; public class MaxSumOperation { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); int[] A = new int[n]; for (int i = 0; i < n; i++) { A[i] = sc.nextInt(); } sc.close(); int[][] dp = new int[n][n]; // 初始化长度为1的区间:只能取单个元素 for (int i = 0; i < n; i++) { dp[i][i] = A[i]; } // 从长度为2的区间开始,逐步计算到整个数组长度 for (int len = 2; len <= n; len++) { for (int i = 0; i <= n - len; i++) { int j = i + len - 1; // 区间的右端点 // 计算四种操作的可能收益 int option1 = dp[i][j-1] + A[j]; // 操作1:取最后一个 int option2 = (len >= 2) ? (dp[i][j-2] + A[j] * A[j-1]) : Integer.MIN_VALUE; // 操作2:取最后两个乘积 int option3 = dp[i+1][j] + A[i]; // 操作3:反转后取最后一个(即取第一个) int option4 = (len >= 2) ? (dp[i+2][j] + A[i] * A[i+1]) : Integer.MIN_VALUE; // 操作4:反转后取最后两个乘积 // 取四个选项中的最大值作为当前区间的最优解 dp[i][j] = Math.max(Math.max(option1, option2), Math.max(option3, option4)); } } System.out.println(dp[0][n-1]); } }
代码说明
- 状态初始化:长度为1的区间只能拿单个元素,所以
dp[i][i] = A[i]。 - 区间递推:从短到长遍历所有可能的区间,确保计算长区间时,它依赖的子区间已经算出最优解。
- 操作对应:把题目中的四种操作转化为对应的子问题,避免了实际反转数组的开销(因为反转后取最后元素等价于取原数组的第一个元素,反转后取最后两个等价于取原数组前两个,不用真的反转)。
用示例输入1 4 2 3 5测试,代码会输出24,和题目中的最优结果一致,其他测试用例也能正确覆盖啦!
内容的提问来源于stack exchange,提问作者Arnav Das
相关产品推荐
相关产品推荐

