【LeetCode-动态规划】最大的以 1 为边界的正方形

题目描述

给你一个由若干 0 和 1 组成的二维网格 grid,请你找出边界全部由 1 组成的最大 正方形 子网格,并返回该子网格中的元素数量。如果不存在,则返回 0。
示例:

输入:grid = [[1,1,1],[1,0,1],[1,1,1]]
输出:9

输入:grid = [[1,1,0,0]]
输出:1

示例:

  • 1 <= grid.length <= 100
  • 1 <= grid[0].length <= 100
  • grid[i][j] 为 0 或 1

题目链接: https://leetcode-cn.com/problems/largest-1-bordered-square/

思路

这题和统计全 1 子矩形有点类似,使用动态规划来做。

  • 状态定义:使用两个状态:left[i][j] 表示位置 (i, j) 左边 1 的个数(包括位置 (i, j)),up[i][j] 表示位置 (i, j) 上边 1 的个数(包括位置 (i, j));
  • 状态转移:如果 grid[i][j]==1,left[i][j] = left[i][j-1] + 1,up[i][j] = up[i-1][j] + 1;
  • 边界条件(grid[i][j]==1 的情况下):
    • i==0 && j==0:left[i][j] = up[i][j] = 1;
    • i==0 && j!=0:left[i][j] = left[i][j-1] + 1; up[i][j] = 1;
    • i!=0 && j==0:left[i][j] = 1; up[i][j] = up[i-1][j] + 1;

对于每一个位置(i, j),left[i][j] 表示该位置左边有多少 1,up[i][j] 表示该位置上边有多少 1,则以位置 (i, j) 为右下角的正方形的最大边长 len = min(left[i][j], up[i][j]),我们需要判断 left[i-k+1][j](右上角) 和 up[i][j-k+1](左下角)1 的个数是不是大于等于 k,k ∈ [1, len],选择最大的 k 作为以位置 (i, j) 为右下角的正方形的边长。在遍历的过程中记录最大边长。代码如下:

class Solution {
public:
    int largest1BorderedSquare(vector<vector<int>>& grid) {
        if(grid.empty()) return 0;

        int m = grid.size();
        int n = grid[0].size();
        vector<vector<int>> left(m, vector<int>(n, 0));
        vector<vector<int>> up(m, vector<int>(n, 0));
        for(int i=0; i<m; i++){
            for(int j=0; j<n; j++){
                if(grid[i][j]==1){
                    if(i==0 && j==0){
                        left[i][j] = 1;
                        up[i][j] = 1;
                    }else if(i==0){
                        left[i][j] = left[i][j-1] + 1;
                        up[i][j] = 1;
                    }else if(j==0){
                        left[i][j] = 1;
                        up[i][j] = up[i-1][j] + 1;
                    }else{
                        left[i][j] = left[i][j-1] + 1;
                        up[i][j] = up[i-1][j] + 1;
                    }
                }
            }
        }

        int ans = 0;
        for(int i=0; i<m; i++){
            for(int j=0; j<n; j++){
                int len = min(left[i][j], up[i][j]);
                for(int k=len; k>0; k--){
                    if(left[i-k+1][j]>=k && up[i][j-k+1]>=k){
                        ans = max(ans, k);
                        break;
                    }
                }
            }
        }
        return ans*ans;
    }
};
  • 时间复杂度:O(mn)
  • 空间复杂度:O(mn)
原文地址:https://www.cnblogs.com/flix/p/13525561.html