针对矩阵的一类动态规划处理

本文解析LeetCode上的两道动态规划矩阵题,一是寻找最大全‘1’子方阵,二是计算任意子矩阵的元素之和。通过具体实例介绍了如何使用动态规划算法解决这两类问题,并给出了详细的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  LeetCode动态规划题目中遇到两道矩阵题,一个是寻找子矩阵,一个是计算某个子矩阵的和。这里介绍一下他们的处理方法,记录思考。

寻找子矩阵

  LeetCode 221 Maximal Square

Given a 2D binary matrix filled with 0’s and 1’s, find the largest square containing only 1’s and return its area.

For example, given the following matrix:

这里写图片描述
Return 4.

  本题要寻找所给矩阵中最大的全‘1’子方阵。三个限制条件:
  ① 最大,即边长最长
  ② 内部全‘1’
  ③ 方阵,长、宽相等
  既然用动态规划算法做,那么就需要考虑使用递归的方法还是非递归的方法。有点像找寻迷宫路线,你是选择从出口往回向入口方向找还是从直接入口开始找。

  • 递归通常是将一个大问题拆解为一个个小问题,再依次拆解下去,遇到边界停止。
  • 非递归通常是从基本的小问题(一般是顺序遍历)着手,累积到形成一个大问题,遍历完或找到答案位置。  

  本题适合哪种呢?先来看看全‘1’方阵是怎么形成的。
  方阵的具体位置其实只需要两个点就可以了——左上角和右下角。这是大多数时的思维。然而本题有了左上角和右下角,依旧还要考虑这两个点之间是否还有‘0’,比较困难。因此本题的方阵适合由小方阵构造而来。
  最小的方阵无疑就是一个‘1’了。边长增加1,意味着对于当前点,其左上角、上方、左方的三个点都应该是‘1’。缺一不可,即有一个‘0’,该方阵都不成立。而如果我们想要知道该方阵的边长呢?其实很简单,针对当前点,从其左上角、上方、左方的三个点中选择一个最小值,再+1,是不是就是以当前点作为右下角的全‘1’方阵的边长呢?这需要另外构建一个矩阵。以题目中的方阵为例,按此法构建后的矩阵如下:
  这里写图片描述
  
  代码如下:

public int maximalSquare(char[][] a) {
    if(a.length == 0) return 0;
    int m = a.length, n = a[0].length, result = 0;
    int[][] b = new int[m+1][n+1];
    for (int i = 1 ; i <= m; i++) {
        for (int j = 1; j <= n; j++) {
            if(a[i-1][j-1] == '1') {
                b[i][j] = Math.min(Math.min(b[i][j-1] , b[i-1][j-1]), b[i-1][j]) + 1;
                result = Math.max(b[i][j], result); // update result
            }
        }
    }
    return result*result;
}

计算子矩阵的和

  LeetCode 304 Range Sum Query 2D - Immutable

Given a 2D matrix matrix, find the sum of the elements inside the rectangle defined by its upper left corner (row1, col1) and lower right corner (row2, col2).

这里写图片描述
The above rectangle (with the red border) is defined by (row1, col1) = (2, 1) and (row2, col2) = (4, 3), which contains sum = 8.

Example:
Given matrix = [
[3, 0, 1, 4, 2],
[5, 6, 3, 2, 1],
[1, 2, 0, 1, 5],
[4, 1, 0, 1, 7],
[1, 0, 3, 0, 5]
]

sumRegion(2, 1, 4, 3) -> 8
sumRegion(1, 1, 2, 2) -> 11
sumRegion(1, 2, 2, 4) -> 12

Note:
You may assume that the matrix does not change.
There are many calls to sumRegion function.
You may assume that row1 ≤ row2 and col1 ≤ col2.

public class NumMatrix {

    public NumMatrix(int[][] matrix) {

    }

    public int sumRegion(int row1, int col1, int row2, int col2) {

    }
}

/**
 * Your NumMatrix object will be instantiated and called as such:
 * NumMatrix obj = new NumMatrix(matrix);
 * int param_1 = obj.sumRegion(row1,col1,row2,col2);
 */

   本题是在所给API限制的情况下,先对所给矩阵matrix进行相应处理,然后仅根据矩阵的顶点值直接得出子矩阵的元素和。
   其实在对矩阵进行预处理的时候,我们有很多种选择,例如最常想到的,让元素[i][j]的值代表从[0][0]到[i][j]的矩阵值的和。这样实行的话,给我任意两个点,就可以知道这两个点之间矩阵的元素和了。
   这里写图片描述
   上图中,矩阵元素和公式:红色=黄色-蓝色-绿色+褐色。
   根据这个公式,代码如下:

private int[][] dp;

public NumMatrix(int[][] matrix) {
    if(   matrix           == null
       || matrix.length    == 0
       || matrix[0].length == 0   ){
        return;   
    }

    int m = matrix.length;
    int n = matrix[0].length;

    dp = new int[m + 1][n + 1];
    for(int i = 1; i <= m; i++){
        for(int j = 1; j <= n; j++){
            dp[i][j] = dp[i - 1][j] + dp[i][j - 1] -dp[i - 1][j - 1] + matrix[i - 1][j - 1] ;
        }
    }
}

public int sumRegion(int row1, int col1, int row2, int col2) {
    int iMin = Math.min(row1, row2);
    int iMax = Math.max(row1, row2);

    int jMin = Math.min(col1, col2);
    int jMax = Math.max(col1, col2);

    return dp[iMax + 1][jMax + 1] - dp[iMax + 1][jMin] - dp[iMin][jMax + 1] + dp[iMin][jMin];    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值