public int maxProduct(int[] nums) {
int len = nums.length;
int min = nums[0];
int max = nums[0];
int ans = nums[0];
for (int i = 1; i < len; i++) {
int curmin = Math.min(nums[i], Math.min(min * nums[i], max * nums[i]));
int curmax = Math.max(nums[i], Math.max(min * nums[i], max * nums[i]));
ans = Math.max(ans, curmax);
min = curmin;
max = curmax;
}
return ans;
}
子序列累加和必须被7整除的最大累加和
给定一个非负数组nums,
可以任意选择数字组成子序列,但是子序列的累加和必须被7整除
返回最大累加和
对数器验证
public static int maxSum1(int[] nums) {
return f(nums, 0, 0);
}
public static int f(int[] nums, int i, int sum) {
if (i == nums.length) {
return sum % 7 == 0 ? sum : 0;
}
return Math.max(f(nums, i + 1, sum), f(nums, i + 1, sum + nums[i]));
}
// 为了测试
// 生成随机数组
public static int[] randomArray(int n, int v) {
int[] ans = new int[n];
for (int i = 0; i < n; i++) {
ans[i] = (int) (Math.random() * v);
}
return ans;
}
public static int maxSum2(int[] nums) {
int n = nums.length;
int[][] dp = new int[n+1][7];
dp[0][0] = 0;
for (int j = 1; j < 7; j++) {
dp[0][j] = -1;
}
for (int i = 1; i <= n; i++) {
int x = nums[i - 1];
int cur = x % 7;
for (int j = 0; j < 7; j++) {
dp[i][j] = dp[i - 1][j];
int need = cur <= j ? j - cur : j - cur + 7;
if (dp[i - 1][need] != -1) {
dp[i][j] = Math.max(dp[i][j], dp[i - 1][need] + x);
}
}
}
return dp[n][0];
}
// 为了测试
// 对数器
public static void main(String[] args) {
int n = 15;
int v = 30;
int testTime = 20000;
System.out.println("测试开始");
for (int i = 0; i < testTime; i++) {
int len = (int) (Math.random() * n) + 1;
int[] nums = randomArray(len, v);
int ans1 = maxSum1(nums);
int ans2 = maxSum2(nums);
if (ans1 != ans2) {
System.out.println("出错了!");
}
}
System.out.println("测试结束");
}
魔法卷轴
给定一个数组nums,其中可能有正、负、0
每个魔法卷轴可以把nums中连续的一段全变成0
你希望数组整体的累加和尽可能大
卷轴使不使用、使用多少随意,但一共只有2个魔法卷轴
请返回数组尽可能大的累加和
public static int maxSum1(int[] nums) {
// 0次魔法卷轴
int p1 = 0;
for (int num : nums) {
p1 += num;
}
int n = nums.length;
// 1次魔法卷轴
int p2 = mustOneScroll(nums, 0, n - 1);
// 2次魔法卷轴
int p3 = Integer.MIN_VALUE;
for (int i = 1; i < n; i++) {
p3 = Math.max(p3, mustOneScroll(nums, 0, i - 1) + mustOneScroll(nums, i, n - 1));
}
return Math.max(p1, Math.max(p2, p3));
}
public static int mustOneScroll(int[] nums, int l, int r) {
int ans = Integer.MIN_VALUE;
for (int a = l; a <= r; a++) {
for (int b = a; b <= r; b++) {
// l .. a.. b.. r
int curAns = 0;
for (int i = l; i < a; i++) {
curAns += nums[i];
}
for (int i = b + 1; i <= r; i++) {
curAns += nums[i];
}
ans = Math.max(ans, curAns);
}
}
return ans;
}
public static int maxSum2(int[] nums) {
int n = nums.length;
if (n == 0) {
return 0;
}
// 0次魔法卷轴
int p1 = 0;
for (int num: nums) {
p1 += num;
}
// 0-i用一次卷轴,最大累加和
int[] prefix = new int[n];
// 前缀和
int presum = nums[0];
// 前缀和最大值
int maxPresum = Math.max(0, nums[0]);
for (int i = 1; i < n; i++) {
prefix[i] = Math.max(prefix[i - 1] + nums[i], maxPresum);
// 当前前缀和
presum += nums[i];
// 当前和以前最大进行Pk
maxPresum = Math.max(maxPresum, presum);
}
// 1次魔法卷轴
int p2 = prefix[n - 1];
// 2次魔法卷轴
int[] suffix = new int[n];
presum = nums[n - 1];
maxPresum = Math.max(0, nums[n - 1]);
for (int i = n - 2; i >= 0; i--) {
suffix[i] = Math.max(suffix[i - 1] + nums[i], maxPresum);
presum += nums[i];
maxPresum = Math.max(maxPresum, presum);
}
int p3 = Integer.MIN_VALUE;
for (int i = 1; i < n; i++) {
p3 = Math.max(p3, prefix[i - 1] + suffix[i]);
}
return Math.max(p1, Math.max(p2, p3));
}
// 为了测试
public static int[] randomArray(int n, int v) {
int[] ans = new int[n];
for (int i = 0; i < n; i++) {
ans[i] = (int) (Math.random() * (v * 2 + 1)) - v;
}
return ans;
}
// 为了测试
public static void main(String[] args) {
int n = 50;
int v = 100;
int testTime = 10000;
System.out.println("测试开始");
for (int i = 0; i < testTime; i++) {
int len = (int) (Math.random() * n);
int[] nums = randomArray(len, v);
int ans1 = maxSum1(nums);
int ans2 = maxSum2(nums);
if (ans1 != ans2) {
System.out.println("出错了!");
}
}
System.out.println("测试结束");
}
public static int[] maxSumOfThreeSubarray(int[] nums, int k) {
int n = nums.length;
// sums[i]以i开头长度为k的子数组的累加和
int[] sums = new int[n];
for (int l = 0,r = 0, sum = 0; r < n; r++) {
sum += nums[r];
if (r - l + 1 == k) {
sums[l] = sum;
sum -= nums[l];
l++;
}
}
// prefix[i]:0-i上长度为k的子数组中,最大累加和是以什么位置开头的
int[] prefix = new int[n];
for (int l = 1, r = k; r < n; l++, r++) {
if (sums[l] > sums[prefix[r - 1]]) {
prefix[r] = l;
} else {
prefix[r] = prefix[r - 1];
}
}
// suffix[i]:i - n-1上拥有最大累加和的子数组, 是以什么位置开头的
int[] suffix = new int[n];
suffix[n - k] = n - k;
for (int l = n - k - 1; l >= 0; l--) {
if (sums[l] >= sums[suffix[l + 1]]) {
suffix[l] = l;
} else {
suffix[l] = suffix[l + 1];
}
}
int a = 0, b = 0, c = 0, max = 0;
for (int i = k, j = 2 * k - 1; j < n - k; i++, j++) {
int p = prefix[i - 1];
int s = suffix[j + 1];
int sum = sums[p] + sums[i] + sums[s];
if (sum > max) {
max = sum;
a = p;
b = i;
c = s;
}
}
return new int[]{a , b, c};
}
可以翻转1次的情况下子数组最大累加和
给定一个数组nums,
现在允许你随意选择数组连续一段进行翻转,也就是子数组逆序的调整
比如翻转[1,2,3,4,5,6]的[2~4]范围,得到的是[1,2,5,4,3,6]
返回必须随意翻转1次之后,子数组的最大累加和
对数器验证
public static int[] randomArray(int n, int v) {
int[] ans = new int[n];
for (int i = 0; i < n; i++) {
ans[i] = (int) (Math.random() * (v * 2 + 1)) - v;
}
return ans;
}
// 暴力方法
// 为了验证
public static int maxSumReverse1(int[] nums) {
int ans = Integer.MIN_VALUE;
for (int l = 0; l < nums.length; l++) {
for (int r = l; r < nums.length; r++) {
reverse(nums, l, r);
ans = Math.max(ans, maxSum(nums));
reverse(nums, l, r);
}
}
return ans;
}
// nums[l...r]范围上的数字进行逆序调整
public static void reverse(int[] nums, int l, int r) {
while (l < r) {
int tmp = nums[l];
nums[l++] = nums[r];
nums[r--] = tmp;
}
}
// 返回子数组最大累加和
public static int maxSum(int[] nums) {
int n = nums.length;
int ans = nums[0];
for (int i = 1, pre = nums[0]; i < n; i++) {
pre = Math.max(nums[i], pre + nums[i]);
ans = Math.max(ans, pre);
}
return ans;
}
public static int maxSumReverse2(int[] nums) {
int n = nums.length;
int[] start = new int[n];
start[n - 1] = nums[n - 1];
// 以i开头,最大累加和
for (int i = n - 2; i >= 0; i--) {
start[i] = Math.max(nums[i], nums[i] + start[i + 1]);
}
int ans = start[0];
// 以i-1结尾,其中的最大累加和
int end = nums[0];
// 各个最大累加和的最大置
int maxEnd = nums[0];
for (int i = 1; i < n; i++) {
ans = Math.max(ans, maxEnd + start[i]);
end = Math.max(nums[i], end + nums[i]);
maxEnd = Math.max(maxEnd, end);
}
ans = Math.max(ans, maxEnd);
return ans;
}
// 为了测试
// 对数器
public static void main(String[] args) {
int n = 50;
int v = 200;
int testTime = 20000;
System.out.println("测试开始");
for (int i = 0; i < testTime; i++) {
int len = (int) (Math.random() * n) + 1;
int[] arr = randomArray(len, v);
int ans1 = maxSumReverse1(arr);
int ans2 = maxSumReverse2(arr);
if (ans1 != ans2) {
System.out.println("出错了!");
}
}
System.out.println("测试结束");
}
删掉1个数字后长度为k的子数组最大累加和
给定一个数组nums,求必须删除一个数字后的新数组中
长度为k的子数组最大累加和,删除哪个数字随意
对数器验证
public static int maxSum1(int[] nums, int k) {
int n = nums.length;
if (n <= k) {
return 0;
}
int ans = Integer.MIN_VALUE;
for (int i = 0; i < n; i++) {
int[] rest = delete(nums, i);
ans = Math.max(ans, lenKmaxSum(rest, k));
}
return ans;
}
public static int lenKmaxSum(int[] nums, int k) {
int n = nums.length;
int ans = Integer.MIN_VALUE;
for (int i = 0; i <= n - k; i++) {
int cur = 0;
for (int j = i, cnt = 0; cnt < k; j++, cnt++) {
cur += nums[j];
}
ans = Math.max(ans, cur);
}
return ans;
}
public static int[] delete(int[] nums, int index) {
int len = nums.length - 1;
int[] ans = new int[len];
int i = 0;
for (int j = 0; j < nums.length; j++) {
if (j != index) {
ans[i++] = nums[j];
}
}
return ans;
}
public static int maxSum2(int[] nums, int k) {
int n = nums.length;
if (n <= k) {
return 0;
}
int[] window = new int[n];
int l = 0;
int r = 0;
long sum = 0;
int ans = Integer.MIN_VALUE;
for (int i = 0; i < n; i++) {
while (l < r && nums[window[r - 1]] >= nums[i]) {
r--;
}
window[r++] = i;
sum += nums[i];
if (i >= k) {
ans = Math.max(ans, (int) (sum - nums[window[l]]));
if (window[l] == i - k) {
l++;
}
sum -= nums[i - k];
}
}
return ans;
}
// 为了测试
// 生成长度为n,值在[-v, +v]之间的随机数组
public static int[] randomArray(int n, int v) {
int[] ans = new int[n];
for (int i = 0; i < n; i++) {
ans[i] = (int) (Math.random() * (2 * v + 1)) - v;
}
return ans;
}
// 为了测试
// 对数器
public static void main(String[] args) {
int n = 200;
int v = 1000;
int testTimes = 10000;
System.out.println("测试开始");
for (int i = 0; i < testTimes; i++) {
int len = (int) (Math.random() * n) + 1;
int[] nums = randomArray(len, v);
int k = (int) (Math.random() * n) + 1;
int ans1 = maxSum1(nums, k);
int ans2 = maxSum2(nums, k);
if (ans1 != ans2) {
System.out.println("出错了!");
}
}
System.out.println("测试结束");
}