【網格 dp】B005_LC_最大的以 1 爲邊界的正方形(暴力 / 考慮木桶原理的 dp)

一、Problem

Given a 2D grid of 0s and 1s, return the number of elements in the largest square subgrid that has all 1s on its border, or 0 if such a subgrid doesn’t exist in the grid.

Example 1:
Input: grid = [[1,1,1],[1,0,1],[1,1,1]]
Output: 9

Example 2:
Input: grid = [[1,1,0,0]]
Output: 1

Constraints:

1 <= grid.length <= 100
1 <= grid[0].length <= 100
grid[i][j] is 0 or 1

二、Solution

方法一:暴力

  • 題目讓你求全以 1 爲邊界的正方形的面積,那我們就只能枚舉邊界呀,還能怎麼做?
  • 首先我們得固定一個起點 (x,y)(x, y)(正方形的左上、右上、左下、右下角都行)
  • 然後再確定一個長度 kk,從而構成一個正方形
  • 最後檢查出該正方形的四條邊都是 1 構成,那麼正方形的面積即可作爲答案之一

這裏是以左上角爲起點

class Solution {
    public int largest1BorderedSquare(int[][] g) {
        int n = g.length, m = g[0].length, mi = Math.min(n, m), ans = 0;

        for (int x = 0; x < n; x++)
        for (int y = 0; y < m; y++) if (g[x][y] == 1) {
            for (int k = 0; k <= mi; k++) {
                int tx = x + k, ty = y + k;		//右下角座標
                if (tx >= n || ty >= m || g[x][ty] == 0 || g[tx][y] == 0)
                    break;
                boolean flag = true;
                for (int c = 0; c <= k && flag; c++) {
                    if (g[tx-c][ty] == 0 || g[tx][ty-c] == 0)
                        flag = false; 
                }
                if (flag)
                    ans = Math.max(ans, k+1);	//優化:(k+1)*(k+1)
            }
        }
        return ans * ans;	
    }
}

小優化:可以將內部計算面積的代碼提到外面,減少乘法運算次數

複雜度分析

  • 時間複雜度:O(n4)O(n^4)
  • 空間複雜度:O(1)O(1)

方法二:dp

這題的 dp 沒想到怎麼列方程以及如何去轉移方程,額, 還是挺難想到的…

  • 定義狀態
    • f[i][j][0]f[i][j][0] 表示以 (i,j)(i, j) 爲右下角且往左數連續 1 的個數
    • f[i][j][1]f[i][j][1] 表示以 (i,j)(i, j) 爲右下角且往上數連續 1 的個數
  • 思考初始化:
    • f[i][j][0...1]=0f[i][j][0...1] = 0
  • 思考狀態轉移方程
    • 如果存在 g[i1][j1]=1g[i-1][j-1] = 1,則有 f[i][j][0]=f[i][j1]+1f[i][j][0] = f[i][j-1] + 1
    • 如果存在 g[i1][j1]=1g[i-1][j-1] = 1,則有 f[i][j][1]=f[i1][j]+1f[i][j][1] = f[i-1][j] + 1
    • 我們上面只是檢查了點 (i,j)(i, j) 的左方和上方兩個方向的連續 11 的個數,但我們還要檢查另外兩條邊;因爲由木桶定理得,一個矩形中的最大正方形面積由矩形 44 條邊的最小邊決定,所以這個檢查是必要的
class Solution {
    public int largest1BorderedSquare(int[][] g) {
        int n = g.length, m = g[0].length, ans = 0, f[][][] = new int[n+1][m+1][2];

        for (int x = 1; x <= n; x++)
        for (int y = 1; y <= m; y++) if (g[x-1][y-1] == 1) {
            f[x][y][0] = f[x][y-1][0] + 1;
            f[x][y][1] = f[x-1][y][1] + 1;
            int k = Math.min(f[x][y-1][0], f[x-1][y][1]);
            while (k > 0) {
                if (f[x-k][y][0] > k && f[x][y-k][1] > k)   // 長度k可以組成
                    break;
                k--;
            }
            ans = Math.max(ans, k+1);
        }
        return ans * ans;
    }
}

複雜度分析

  • 時間複雜度:O(n2)O(n^2)
  • 空間複雜度:O(n2)O(n^2)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章