針對矩陣的一類動態規劃處理

  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];    
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章