71:子数组最大和与拓展

    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("测试结束");
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值