53.最大子数组和
dp[i] 表示以 nums[i - 1] 结尾的子数组的最大和
dp[i + 1] = max(nums[i], dp[i] + nums[i])
class Solution {
public int maxSubArray(int[] nums) {
int pre = 0, res = nums[0];
for (int x : nums){
pre = Math.max(x, pre + x);
// pre = pre < 0 ? x : pre + x;
res = Math.max(res, pre);
}
return res;
}
}
记忆化搜索
class Solution {
int INF = -0x3f3f3f3f;
int[] nums;
int[][] memo;
public int maxSubArray(int[] nums) {
int n = nums.length;
this.nums = nums;
memo = new int[n][2];
for (int[] a : memo) Arrays.fill(a, INF);
// 选与不选 1/0
return Math.max(dfs(n - 1, 0), dfs(n - 1, 1));
}
int dfs(int i, int j) {
if (i < 0) return INF;
if (memo[i][j] != INF) return memo[i][j];
int res = INF;
// 选 i
if (j == 1) res = Math.max(nums[i], dfs(i - 1, 1) + nums[i]);
else res = Math.max(dfs(i - 1, 1), dfs(i - 1, 0));
return memo[i][j] = res;
}
}
918.环形子数组的最大和
premax/premin:前 i 个元素的子数组的和的最大/小值
maxsum/minsum:数组中子数组和的最大/小值
无环,即为 53. 最大子数组和
有环,分首尾两段,要使两端之和最大,必须让中间的子数组和最小,即最大子数组和为:sum(nums) - minsum,问题转换为求最小子数组和。
class Solution {
public int maxSubarraySumCircular(int[] nums) {
int n = nums.length, sum = 0, maxsum = nums[0], minsum = nums[0], premax = 0, premin = 0;
for (int x : nums){
premax = Math.max(x, premax + x);
premin = Math.min(x, premin + x);
maxsum = Math.max(maxsum, premax);
minsum = Math.min(minsum, premin);
sum += x;
}
return Math.max(maxsum, sum != minsum ? sum - minsum : maxsum);
// [-3,-2,-3] 最小和 = 和 的情况
}
}
前缀和、单调队列,滑动窗口。
class Solution {
public int maxSubarraySumCircular(int[] nums) {
int n = nums.length;
int[] acc = new int[2 * n];
acc[0] = nums[0];
int res = nums[0];
Deque<Integer> q = new ArrayDeque<>();
q.add(0);
for (int i = 1; i < 2*n; i++){
acc[i] = acc[i - 1] + nums[i % n];
if (i - q.peek() > n) q.poll();
res = Math.max(res, acc[i] - acc[q.peek()]); // 栈顶尽可能小
while (!q.isEmpty() && acc[i] <= acc[q.peekLast()]) q.pollLast();
q.add(i);
}
return res;
}
}
2606. 找到最大开销的子字符串
class Solution {
public int maximumCostSubstring(String s, String chars, int[] vals) {
var v = new int[26];
for (int i = 0; i < 26; ++i)
v[i] = i + 1;
for (int i = 0; i < vals.length; ++i)
v[chars.charAt(i) - 'a'] = vals[i];
// 最大子段和(允许子数组为空)
int ans = 0, f = 0;
for (char c : s.toCharArray()) {
f = Math.max(f, 0) + v[c - 'a'];
ans = Math.max(ans, f);
}
return ans;
}
}
2321. 拼接数组的最大分数
class Solution {
public int maximumsSplicedArray(int[] nums1, int[] nums2) {
return Math.max(solve(nums1, nums2), solve(nums2, nums1));
}
int solve(int[] nums1, int[] nums2) {
int sum = 0, maxSum = 0;
for (int i = 0, s = 0; i < nums1.length; ++i) {
sum += nums1[i];
s = Math.max(s + nums2[i] - nums1[i], 0);
maxSum = Math.max(maxSum, s);
}
return sum + maxSum;
}
}
1186. 删除一次得到子数组最大和
class Solution {
int[] arr;
int[][] memo;
int INF = -0x3f3f3f3f;
public int maximumSum(int[] arr) {
this.arr = arr;
int n = arr.length, res = INF;
memo = new int[n][2];
for (int[] a : memo) Arrays.fill(a, INF);
for (int i = 0; i < n; i++)
res = Math.max(res, Math.max(dfs(i, 0), dfs(i, 1)));
return res;
}
int dfs(int i, int j) {
if (i < 0) return INF;
if (memo[i][j] != INF) return memo[i][j];
if (j == 0) return memo[i][j] = Math.max(dfs(i - 1, 0), 0) + arr[i];
return memo[i][j] = Math.max(dfs(i - 1, 1) + arr[i], dfs(i - 1, 0));
}
}
class Solution {
public int maximumSum(int[] arr) {
int ans = Integer.MIN_VALUE, n = arr.length;
var f = new int[n + 1][2];
Arrays.fill(f[0], Integer.MIN_VALUE / 2); // 除 2 防止负数相加溢出
for (int i = 0; i < n; i++) {
f[i + 1][0] = Math.max(f[i][0], 0) + arr[i];
f[i + 1][1] = Math.max(f[i][1] + arr[i], f[i][0]);
ans = Math.max(ans, Math.max(f[i + 1][0], f[i + 1][1]));
}
return ans;
}
}
363. 矩形区域不超过 K 的最大数值和