class070 子数组最大累加和问题与扩展-上【算法】

简介: class070 子数组最大累加和问题与扩展-上【算法】

class070 子数组最大累加和问题与扩展-上【算法】

code1 53. 最大子数组和

// 累加和最大子数组和

// 给你一个整数数组 nums

// 请你找出一个具有最大累加和的非空子数组

// 返回其最大累加和

// 测试链接 : https://leetcode.cn/problems/maximum-subarray/

dp[i]:以i结尾的子数组[…i]的最大累加和

(1) nums[i] (2) dp[i-1]+nums[i] 二者取最大的

返回Max(dp[…])

code1 动态规划

code2 空间压缩

package class070;
// 子数组最大累加和
// 给你一个整数数组 nums
// 返回非空子数组的最大累加和
// 测试链接 : https://leetcode.cn/problems/maximum-subarray/
public class Code01_MaximumSubarray {
  // 动态规划
  public static int maxSubArray1(int[] nums) {
    int n = nums.length;
    // dp[i] : 子数组必须以i位置的数做结尾,往左能延伸出来的最大累加和
    int[] dp = new int[n];
    dp[0] = nums[0];
    int ans = nums[0];
    for (int i = 1; i < n; i++) {
      dp[i] = Math.max(nums[i], dp[i - 1] + nums[i]);
      ans = Math.max(ans, dp[i]);
    }
    return ans;
  }
  // 空间压缩
  public static int maxSubArray2(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;
  }
  // 如下代码为附加问题的实现
  // 子数组中找到拥有最大累加和的子数组
  // 并返回如下三个信息:
  // 1) 最大累加和子数组的开头left
  // 2) 最大累加和子数组的结尾right
  // 3) 最大累加和子数组的累加和sum
  // 如果不止一个子数组拥有最大累加和,那么找到哪一个都可以
  public static int left;
  public static int right;
  public static int sum;
  // 找到拥有最大累加和的子数组
  // 更新好全局变量left、right、sum
  // 上游调用函数可以直接使用这三个变量
  // 相当于返回了三个值
  public static void extra(int[] nums) {
    sum = Integer.MIN_VALUE;
    for (int l = 0, r = 0, pre = Integer.MIN_VALUE; r < nums.length; r++) {
      if (pre >= 0) {
        // 吸收前面的累加和有利可图
        // 那就不换开头
        pre += nums[r];
      } else {
        // 吸收前面的累加和已经无利可图
        // 那就换开头
        pre = nums[r];
        l = r;
      }
      if (pre > sum) {
        sum = pre;
        left = l;
        right = r;
      }
    }
  }
}

code2 198. 打家劫舍

// 数组中不能选相邻元素的最大累加和

// 给定一个数组,可以随意选择数字

// 但是不能选择相邻的数字,返回能得到的最大累加和

// 测试链接 : https://leetcode.cn/problems/house-robber/

dp[i]:表示[0…i]这个范围上不能选相邻元素的最大累加和

  1. 不要[i],dp[i-1]
  2. 要[i] a.nums[i] b.dp[i-2]+nums[i]

code1 动态规划

code2 空间压缩

package class070;
// 数组中不能选相邻元素的最大累加和
// 给定一个数组,可以随意选择数字
// 但是不能选择相邻的数字,返回能得到的最大累加和
// 测试链接 : https://leetcode.cn/problems/house-robber/
public class Code02_HouseRobber {
  // 动态规划
  public static int rob1(int[] nums) {
    int n = nums.length;
    if (n == 1) {
      return nums[0];
    }
    if (n == 2) {
      return Math.max(nums[0], nums[1]);
    }
    // dp[i] : nums[0...i]范围上可以随意选择数字,但是不能选相邻数,能得到的最大累加和
    int[] dp = new int[n];
    dp[0] = nums[0];
    dp[1] = Math.max(nums[0], nums[1]);
    for (int i = 2; i < n; i++) {
      dp[i] = Math.max(dp[i - 1], Math.max(nums[i], dp[i - 2] + nums[i]));
    }
    return dp[n - 1];
  }
  // 空间压缩
  public static int rob2(int[] nums) {
    int n = nums.length;
    if (n == 1) {
      return nums[0];
    }
    if (n == 2) {
      return Math.max(nums[0], nums[1]);
    }
    int prepre = nums[0];
    int pre = Math.max(nums[0], nums[1]);
    for (int i = 2, cur; i < n; i++) {
      cur = Math.max(pre, Math.max(nums[i], prepre + nums[i]));
      prepre = pre;
      pre = cur;
    }
    return pre;
  }
}

code3 918. 环形子数组的最大和

// 环形数组的子数组最大累加和

// 给定一个数组nums,长度为n

// nums是一个环形数组,下标0和下标n-1是连在一起的

// 返回环形数组中,子数组最大累加和

// 测试链接 : https://leetcode.cn/problems/maximum-sum-circular-subarray/

(1) 答案不被隔断,同1普通数组,最大累加和maxSum

(2) 答案被隔断,整体累加和all-最小累加和minSum

两者取较大即为答案

package class070;
// 环形数组的子数组最大累加和
// 给定一个数组nums,长度为n
// nums是一个环形数组,下标0和下标n-1是连在一起的
// 返回环形数组中,子数组最大累加和
// 测试链接 : https://leetcode.cn/problems/maximum-sum-circular-subarray/
public class Code03_MaximumSumCircularSubarray {
  public static int maxSubarraySumCircular(int[] nums) {
    int n = nums.length, all = nums[0], maxsum = nums[0], minsum = nums[0];
    for (int i = 1, maxpre = nums[0], minpre = nums[0]; i < n; i++) {
      all += nums[i];
      maxpre = Math.max(nums[i], nums[i] + maxpre);
      maxsum = Math.max(maxsum, maxpre);
      minpre = Math.min(nums[i], nums[i] + minpre);
      minsum = Math.min(minsum, minpre);
    }
    // 1) maxsum
    // 2) all - minsum
    return all == minsum ? maxsum : Math.max(maxsum, all - minsum);
  }
}

code4 213. 打家劫舍 II

// 环形数组中不能选相邻元素的最大累加和

// 给定一个数组nums,长度为n

// nums是一个环形数组,下标0和下标n-1是连在一起的

// 可以随意选择数字,但是不能选择相邻的数字

// 返回能得到的最大累加和

// 测试链接 : https://leetcode.cn/problems/house-robber-ii/

思路:

不要[0]位置 [1…n-1]

要[0]位置 nums[0] + [2…n-2]

package class070;
// 环形数组中不能选相邻元素的最大累加和
// 给定一个数组nums,长度为n
// nums是一个环形数组,下标0和下标n-1是连在一起的
// 可以随意选择数字,但是不能选择相邻的数字
// 返回能得到的最大累加和
// 测试链接 : https://leetcode.cn/problems/house-robber-ii/
public class Code04_HouseRobberII {
  public static int rob(int[] nums) {
    int n = nums.length;
    if (n == 1) {
      return nums[0];
    }
    return Math.max(best(nums, 1, n - 1), nums[0] + best(nums, 2, n - 2));
  }
  // nums[l....r]范围上,没有环形的概念
  // 返回 : 可以随意选择数字,但不能选择相邻数字的情况下,最大累加和
  public static int best(int[] nums, int l, int r) {
    if (l > r) {
      return 0;
    }
    if (l == r) {
      return nums[l];
    }
    if (l + 1 == r) {
      return Math.max(nums[l], nums[r]);
    }
    int prepre = nums[l];
    int pre = Math.max(nums[l], nums[l + 1]);
    for (int i = l + 2, cur; i <= r; i++) {
      cur = Math.max(pre, nums[i] + Math.max(0, prepre));
      prepre = pre;
      pre = cur;
    }
    return pre;
  }
}

code5 2560. 打家劫舍 IV

// 打家劫舍 IV

// 沿街有一排连续的房屋。每间房屋内都藏有一定的现金

// 现在有一位小偷计划从这些房屋中窃取现金

// 由于相邻的房屋装有相互连通的防盗系统,所以小偷不会窃取相邻的房屋

// 小偷的 窃取能力 定义为他在窃取过程中能从单间房屋中窃取的 最大金额

// 给你一个整数数组 nums 表示每间房屋存放的现金金额

// 第i间房屋中放有nums[i]的钱数

// 另给你一个整数k,表示小偷需要窃取至少 k 间房屋

// 返回小偷需要的最小窃取能力值

// 测试链接 : https://leetcode.cn/problems/house-robber-iv/

二分答案法

能力[min,max]有单调性

布尔函数判断能力值给定,是否能偷够k间

dp[i]:[0…i]范围上不偷相邻房间并且有能力要求的最大间数

不偷[i],dp[i-1]

能力大于[i],偷[i] dp[i-2]+1

偷不了[i] dp[i-2]

贪心优化:

能偷就偷,偷了就跳过,

尽早偷,留下更大的区间

code1 动态规划

code2 空间压缩

code3 贪心优化

package class070;
// 打家劫舍 IV
// 沿街有一排连续的房屋。每间房屋内都藏有一定的现金
// 现在有一位小偷计划从这些房屋中窃取现金
// 由于相邻的房屋装有相互连通的防盗系统,所以小偷不会窃取相邻的房屋
// 小偷的 窃取能力 定义为他在窃取过程中能从单间房屋中窃取的 最大金额
// 给你一个整数数组 nums 表示每间房屋存放的现金金额
// 第i间房屋中放有nums[i]的钱数
// 另给你一个整数k,表示小偷需要窃取至少 k 间房屋
// 返回小偷需要的最小窃取能力值
// 测试链接 : https://leetcode.cn/problems/house-robber-iv/
public class Code05_HouseRobberIV {
  public static int minCapability(int[] nums, int k) {
    int n = nums.length, l = nums[0], r = nums[0];
    for (int i = 1; i < n; i++) {
      l = Math.min(l, nums[i]);
      r = Math.max(r, nums[i]);
    }
    // l....r
    int m, ans = 0;
    while (l <= r) {
      m = (l + r) / 2;
      if (mostRob1(nums, n, m) >= k) {
        ans = m;
        r = m - 1;
      } else {
        l = m + 1;
      }
    }
    return ans;
  }
  // 盗贼能力为ability时
  // 返回盗贼最多能窃取多少间房屋
  // 注意限制 : 不能窃取相邻房屋
  public static int mostRob1(int[] nums, int n, int ability) {
    if (n == 1) {
      return nums[0] <= ability ? 1 : 0;
    }
    if (n == 2) {
      return (nums[0] <= ability || nums[1] <= ability) ? 1 : 0;
    }
    int[] dp = new int[n];
    dp[0] = nums[0] <= ability ? 1 : 0;
    dp[1] = (nums[0] <= ability || nums[1] <= ability) ? 1 : 0;
    for (int i = 2; i < n; i++) {
      dp[i] = Math.max(dp[i - 1], (nums[i] <= ability ? 1 : 0) + dp[i - 2]);
    }
    return dp[n - 1];
  }
  // 继续空间压缩优化
  public static int mostRob2(int[] nums, int n, int ability) {
    if (n == 1) {
      return nums[0] <= ability ? 1 : 0;
    }
    if (n == 2) {
      return (nums[0] <= ability || nums[1] <= ability) ? 1 : 0;
    }
    int prepre = nums[0] <= ability ? 1 : 0;
    int pre = (nums[0] <= ability || nums[1] <= ability) ? 1 : 0;
    for (int i = 2, cur; i < n; i++) {
      cur = Math.max(pre, (nums[i] <= ability ? 1 : 0) + prepre);
      prepre = pre;
      pre = cur;
    }
    return pre;
  }
  // 继续贪心优化
  public static int mostRob3(int[] nums, int n, int ability) {
    int ans = 0;
    for (int i = 0; i < n; i++) {
      if (nums[i] <= ability) {
        ans++;
        i++;
      }
    }
    return ans;
  }
}

code6 面试题 17.24. 最大子矩阵

// 子矩阵最大累加和问题

// 给定一个二维数组grid,找到其中子矩阵的最大累加和

// 返回拥有最大累加和的子矩阵左上角和右下角坐标

// 如果有多个子矩阵都有最大累加和,返回哪一个都可以

// 测试链接 : https://leetcode.cn/problems/max-submatrix-lcci/

package class070;
import java.util.Arrays;
// 子矩阵最大累加和问题
// 给定一个二维数组grid,找到其中子矩阵的最大累加和
// 返回拥有最大累加和的子矩阵左上角和右下角坐标
// 如果有多个子矩阵都有最大累加和,返回哪一个都可以
// 测试链接 : https://leetcode.cn/problems/max-submatrix-lcci/
public class Code06_MaximumSubmatrix {
  // 如果行和列的规模都是n,时间复杂度O(n^3),最优解了
  public static int[] getMaxMatrix(int[][] grid) {
    int n = grid.length;
    int m = grid[0].length;
    int max = Integer.MIN_VALUE;
    int a = 0, b = 0, c = 0, d = 0;
    int[] nums = new int[m];
    for (int up = 0; up < n; up++) {
      Arrays.fill(nums, 0);
      for (int down = up; down < n; down++) {
        // 如下代码就是题目1的附加问题 :
        // 子数组中找到拥有最大累加和的子数组
        for (int l = 0, r = 0, pre = Integer.MIN_VALUE; r < m; r++) {
          nums[r] += grid[down][r];
          if (pre >= 0) {
            pre += nums[r];
          } else {
            pre = nums[r];
            l = r;
          }
          if (pre > max) {
            max = pre;
            a = up;
            b = l;
            c = down;
            d = r;
          }
        }
      }
    }
    return new int[] { a, b, c, d };
  }
}


相关文章
|
4月前
|
设计模式 算法 Java
【数据结构和算法】删掉一个元素以后全为 1 的最长子数组
这是力扣的 1493 题,难度为中等,解题方案有很多种,本文讲解我认为最奇妙的一种。又又又是一道滑动窗口的典型例题,可以帮助我们巩固滑动窗口算法。这道题很活灵活现,需要加深对题意的变相理解。给你一个二进制数组nums,你需要从中删掉一个元素。 请你在删掉元素的结果数组中,返回最长的且只包含 1 的非空子数组的长度。 如果不存在这样的子数组,请返回 0 。
66 1
|
5月前
|
算法
class072 最长递增子序列问题与扩展【算法】
class072 最长递增子序列问题与扩展【算法】
26 0
|
5月前
|
算法
class071 子数组最大累加和问题与扩展-下【算法】
class071 子数组最大累加和问题与扩展-下【算法】
27 0
|
3月前
|
算法
|
4月前
|
设计模式 算法 Java
【数据结构和算法】子数组最大平均数 I
​ 原题链接:力扣 643 题 子数组最大平均数 I 给你一个由n个元素组成的整数数组nums和一个整数k。 请你找出平均数最大且长度为k的连续子数组,并输出该最大平均数。 任何误差小于10-5的答案都将被视为正确答案。 ​
45 3
|
3月前
|
算法 搜索推荐 程序员
C语言第三十三练—— KMP算法和扩展 KMP算法
C语言第三十三练—— KMP算法和扩展 KMP算法
35 0
|
3月前
|
算法 搜索推荐 程序员
C语言第二十二练——扩展欧几里得算法
C语言第二十二练——扩展欧几里得算法
30 0
|
4月前
|
算法 测试技术 C#
【滑动窗口】C++算法:K 个不同整数的子数组
【滑动窗口】C++算法:K 个不同整数的子数组
|
4月前
|
算法 测试技术 C#
【滑动窗口】【二分查找】C++算法:和至少为 K 的最短子数组
【滑动窗口】【二分查找】C++算法:和至少为 K 的最短子数组
|
4月前
|
算法
算法题解-长度最小的子数组
算法题解-长度最小的子数组