问题描述
小C拿到了一个数组,他可以进行最多一次操作:将一个元素修改为任意给定的xx。小C想知道,经过这次修改后,能够得到的连续子数组的最大和是多少。
解题思路
-
Kadane算法:首先,我们可以使用Kadane算法来计算不进行任何修改时的最大子数组和。Kadane算法的时间复杂度为O(n)。
-
考虑修改操作:接下来,我们需要考虑如何利用一次修改操作来最大化子数组的和。我们可以遍历数组中的每个元素,假设将其修改为
x,然后计算修改后的最大子数组和。 -
计算修改后的最大和:对于每个元素
a[i],我们可以计算以下三种情况的最大和:- 不修改任何元素,直接使用Kadane算法的结果。
- 修改
a[i]为x,然后计算包含a[i]的最大子数组和。 - 修改
a[i]为x,然后计算不包含a[i]的最大子数组和。
-
取最大值:最终的结果是上述三种情况的最大值。
代码实现
public class Main {
public static int solution(int n, int x, int[] a) {
// 使用Kadane算法计算不修改时的最大子数组和
int maxSumWithoutChange = kadane(a);
int maxSumWithChange = Integer.MIN_VALUE;
// 遍历数组中的每个元素,假设将其修改为 x
for (int i = 0; i < n; i++) {
int originalValue = a[i];
a[i] = x; // 修改当前元素为 x
// 计算修改后的最大子数组和
int currentMaxSum = kadane(a);
// 恢复原值
a[i] = originalValue;
// 更新最大值
maxSumWithChange = Math.max(maxSumWithChange, currentMaxSum);
}
// 返回不修改和修改后的最大值
return Math.max(maxSumWithoutChange, maxSumWithChange);
}
// Kadane算法计算最大子数组和
private static int kadane(int[] a) {
int maxEndingHere = a[0];
int maxSoFar = a[0];
for (int i = 1; i < a.length; i++) {
maxEndingHere = Math.max(a[i], maxEndingHere + a[i]);
maxSoFar = Math.max(maxSoFar, maxEndingHere);
}
return maxSoFar;
}
public static void main(String[] args) {
System.out.println(solution(5, 10, new int[] { 5, -1, -5, -3, 2 }) == 15);
System.out.println(solution(2, -3, new int[] { -5, -2 }) == -2);
System.out.println(solution(6, 10, new int[] { 4, -2, -11, -1, 4, -1 }) == 15);
}
}
Kadane算法介绍
Kadane's 算法是一种用于解决最大子数组和问题的高效算法。这个问题要求在一个整数数组中找到一个连续子数组(至少包含一个数字),使得该子数组的元素之和最大。例如,给定数组 [-2, 1, -3, 4, -1, 2, 1, -5, 4],其连续子数组 [4, -1, 2, 1] 的和为 6,这是所有可能的子数组中最大的。
Kadane's 算法的核心思想
- 局部最优解:在遍历过程中,我们维护一个变量
current_max来记录当前子数组的最大和。 - 全局最优解:同时维护另一个变量
global_max来记录到目前为止遇到的最大子数组和。 - 在每一步,我们决定是否将当前元素加入现有的子数组中,或者从当前元素开始一个新的子数组。
算法步骤
- 初始化两个变量
current_max和global_max,都设置为数组的第一个元素。 - 遍历数组中的每个元素:
- 对于每个元素,更新
current_max为其自身与current_max + 当前元素中较大的值。 - 如果
current_max大于global_max,则更新global_max。
- 对于每个元素,更新
- 遍历结束后,
global_max即为所求的最大子数组和。
Java 实现示例
public class KadanesAlgorithm {
public static int maxSubArraySum(int[] nums) {
if (nums == null || nums.length == 0) {
throw new IllegalArgumentException("Array must not be null or empty");
}
// 初始化当前最大子数组和以及全局最大子数组和
int currentMax = nums[0];
int globalMax = nums[0];
// 从第二个元素开始遍历
for (int i = 1; i < nums.length; i++) {
// 更新当前最大子数组和
currentMax = Math.max(nums[i], currentMax + nums[i]);
// 更新全局最大子数组和
if (currentMax > globalMax) {
globalMax = currentMax;
}
}
return globalMax;
}
public static void main(String[] args) {
int[] nums = {-2, 1, -3, 4, -1, 2, 1, -5, 4};
System.out.println("Maximum subarray sum is: " + maxSubArraySum(nums));
}
}
在这个实现中:
currentMax被用来跟踪当前子数组的最大和。globalMax保存了迄今为止发现的最大子数组和。- 每次迭代时,我们决定是继续累加当前元素还是重新开始一个新的子数组,这取决于
currentMax + nums[i]和nums[i]哪个更大。
时间复杂度与空间复杂度
- 时间复杂度:O(n),因为算法只需要遍历整个数组一次。
- 空间复杂度:O(1),因为我们只使用了常数级别的额外空间。