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.

Example:

Input: 

1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0

Output: 4

思路:

    题目的重点就是,必须是一个正方形内都是1,而且题目给的不是 int 的 vector,是 char 的 vector 需要注意。

    看到这种题第一个想法就是先用暴力方式做一次,练练手,然后再用别的方法,我的暴力算方法AC代码如下:

int maximalSquare(vector<vector<char>>& matrix)
{
    int m = matrix.size();
    if (m == 0)
        return 0;
    int n = matrix[0].size();   // m行n列
    int max_square = 0;
    for (int i = 0; i < m; i++)
    {
        for (int j = 0; j < n; j++)
        {
            if (matrix[i][j] == '1')
            {
                int tmp_len = 1;    // square 限制了只能是正方形,所以只需要记录一个边长
                bool flag = true;
                while (i + tmp_len < m && j + tmp_len < n && flag)
                {
                    for (int t = 0; t <= tmp_len; t++)
                    {
                        if (matrix[i + tmp_len][j + t] == '0' 
                        || matrix[i + t][j + tmp_len] == '0')
                        {
                            flag = false;
                            break;
                        }
                    }
                    if (flag == true)
                        ++tmp_len;
                }
                max_square = max(max_square, tmp_len * tmp_len);
            }
        }
    }
    return max_square;  // 当没有1的时候,返回0
}

    没有想到的是,这种方法12ms,击败了 98.72 的AC代码!!!

    然后基于上边的代码,我又想了想加速的方式,一个是,没必要每次计算面积,只需要每次记录边长,最后做一次乘法返回即可,这样能少做很多次乘法;第二点是,当比如说我目前的最大边长是100的情况下, 倒数99行以及倒数99列之后的都不需要遍历了,因为不可能比目前的大了,这样按理说能加速不少。AC代码如下:

int maximalSquare(vector<vector<char>>& matrix)
{
    int m = matrix.size();
    if (m == 0)
        return 0;
    int n = matrix[0].size();   // m行n列
    int max_len = 0;         // 当没有1的时候,返回0
    for (int i = 0; i < m - max_len; i++)       // 根据目前最大边长,减少遍历范围!
    {
        for (int j = 0; j < n - max_len; j++)
        {
            if (matrix[i][j] == '1')
            {
                int tmp_len = 1;    // square 限制了只能是正方形,所以只需要记录一个边长
                bool flag = true;
                while (i + tmp_len < m && j + tmp_len < n && flag)
                {
                    for (int t = 0; t <= tmp_len; t++)
                    {
                        if (matrix[i + tmp_len][j + t] == '0' || matrix[i + t][j + tmp_len] == '0')
                        {
                            flag = false;
                            break;
                        }
                    }
                    if (flag == true)
                        ++tmp_len;
                }
                max_len = max(max_len, tmp_len);    // 每次不算面积,最后再算,能加速一些
            }
        }
    }
    return max_len * max_len;
}

    然而神奇的是,这样的结果是28ms,只击败7%的提交。很是不解= =,望大神指点(我觉得是测试样例的问题吧,暴力破解能击败98%的人也是不敢相信)。

    暴力方法过了,就要想想其他的方式了,这种 m*n 的矩阵内求最值得问题,应该都能用DP来做。

    首先是创建一个和原始矩阵一样大小的矩阵 dp(其实行列都是要大1的,为了方便),里边的元素值全设为0,然后从左上遍历到右下,原始矩阵中的0不处理,对应dp矩阵中也就是0不变,对原始矩阵中的1,dp矩阵中对应值按一下方式计算:

                             dp(i, j) = min(dp(i−1, j), dp(i−1, j−1), dp(i, j−1)) + 1

    这样就能够得到以每个位置为右下角的最大的全1矩阵边长,如下图所示:

    dp矩阵中的“3”就表示,以这个位置为右下角,可以有边长为3的全1矩阵,在原始就很中可以很清楚的看到。这种方式就不能用上边的减少遍历范围的方式加速,因为不到最后边不知道会不会有更大的。

    AC代码如下:

int maximalSquare(vector<vector<char>>& matrix)
{
    int m = matrix.size();
    if(m == 0)
        return 0;
    int n = matrix[0].size();   // m行n列
    vector<vector<int>> dp = vector<vector<int>>(m + 1, vector<int>(n + 1, 0));
    int max_len = 0;
    for (int i = 1; i <= m; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            if (matrix[i-1][j-1] == '1')
            {
                dp[i][j] = min(min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1;
                max_len = max(max_len, dp[i][j]);
            }
        }
    }
    return max_len * max_len;
}

    这个代码也是98%的击败,按理说肯定是比暴力要快很多的。

    网上还有用更小的空间复杂度的代码,但是我感觉空间相对于时间来说并不重要,所以,就这样吧 = =。

猜你喜欢

转载自blog.csdn.net/Bob__yuan/article/details/81391616