每日一题 -- LeetCode17.24

100 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情image.png

前言

每日一题,轻松解题

每日一题为刷题系列 每日刷一题LeetCode题,并且对题目进行分析,分享思路。

正文

:最大子矩阵

难度:困难

题目要求:

给定一个正整数、负整数和 0 组成的 N × M 矩阵,编写代码找出元素总和最大的子矩阵。

返回一个数组 [r1, c1, r2, c2],其中 r1, c1 分别代表子矩阵左上角的行号和列号,r2, c2 分别代表右下角的行号和列号。若有多个满足条件的子矩阵,返回任意一个均可。

举个例子

输入: [   [-1,0],
   [0,-1]
]
输出: [0,1,0,1]
解释: 输入中标粗的元素即为输出所表示的矩阵

:解题

方法一 :动态规划(DP)

动态规划(DP)

动态规划过程是:每次决策依赖于当前状态,又随即引起状态的转移。一个决策序列就是在变化的状态中产生出来的,所以,这种多阶段最优化决策解决问题的过程就称为动态规划。

基本思想与分治法类似,也是将待求解的问题分解为若干个子问题(阶段),按顺序求解子阶段,前一子问题的解,为后一子问题的求解提供了有用的信息。在求解任一子问题时,列出各种可能的局部解,通过决策保留那些有可能达到最优的局部解,丢弃其他局部解。依次解决各子问题,最后一个子问题就是初始问题的解。

解题思路:

  • 将二维数组打平,再求最大连续和。

  • 这里使用了三个函数去处理取值:

  • dp:接收一个数组,返回该数组的最大连续和,并返回这个和的起止坐标。

  • countArr:将一个二维数组打平,每一列进行累加,变成一个数组。

  • 循环:遍历循环所有矩阵,拿到每个矩阵的最大值。

  • 最后返回最大的那个的起止坐标。

编辑代码:

function getMaxMatrix(matrix: number[][]) {

  /**
   * 计算平面数组的最大连续和,并返回该和的起止坐标
   * @param nums 
   * @returns 
   */
  const dp = (nums: number[]): {
    val: number,
    index: number[]
  } => {
    const len = nums.length
    const dp = [...nums]
    const length = new Array(len).fill(1)

    for(let i = 1; i < len; i++) {
      if(dp[i - 1] + dp[i] > dp[i]) {
        dp[i] = dp[i - 1] + dp[i]
        length[i] += length[i - 1]
      }
    }

    const max = Math.max(...dp)
    let k: number
    for(let i = 0; i < len; i++) {
      if(dp[i] === max) {
        k = i
        break
      }
    }
    
    return {
      val: dp[k!],
      index: [k! - length[k!] + 1, k!]
    }
  }

  /**
   * 将二维数组压平,转化为一维数组
   * @param matrix 
   * @returns 
   */
  const countArr = (matrix: number[][]): number[] => {
    if(matrix.length === 1) return matrix[0]
    let arr = new Array(matrix[0].length).fill(0)
    
    for(let row = 0; row < matrix.length; row++) {
      for(let col = 0; col < matrix[0].length; col++) {
        arr[col] += matrix[row][col]
      }
    }

    return arr
  }

  const len = matrix.length
  const map = new Map<number, number[]>()
  const set = []
  for(let i = 0; i < len; i++) {
    for(let j = 0; j <= i; j++) {
      // 二维转一维
      const tmp = dp(countArr(matrix.slice(j, i+1)))
      map.set(tmp.val, [j, tmp.index[0], i, tmp.index[1]])
      set.push(tmp.val)
    }
  }

  const max = Math.max(...set)
  let result: number[]
  map.forEach((item, key) => {
    if(key === max) result = item
  })
  return result!
};

总结

无论做什么分析最重要,其中我们分析了题目,分析了解题思路,其实在分析完解题思路后,代码其实就是很简单的事情了,养成习惯,无论做什么之前,都要进行分析,这样有助于你更快更好的完成这件事。