邊界都是1的最大正方形大小

瘋人張發表於2020-11-14
邊界都是1的最大正方形大小

題目描述

給定一個 N × N N \times N N×N的矩陣matrix,在這個矩陣中,只有0和1兩種值,返回邊框全是1的最大正方形的邊長長度、

例如

0 1 1 1 1

0 1 0 0 1

0 1 0 0 1

0 1 1 1 1

0 1 0 1 1

其中,邊框全是1的最大正方形的大小為 4 × 4 4 \times 4 4×4,所以返回4

[要求]

時間複雜度為 O ( n 3 ) O(n^3) O(n3),空間複雜度為 O ( n 2 ) O(n^2) O(n2)

輸入描述:

第一行一個整數N。表示矩陣的長寬。
接下來N行,每行N個整數表示矩陣內的元素

輸出描述:

輸出一個整數表示答案

示例1
輸入
5
0 1 1 1 1
0 1 0 0 1
0 1 0 0 1
0 1 1 1 1
0 1 0 1 1
輸出
4
備註:

1 ⩽ N ⩽ 200 1 \leqslant N \leqslant 200 1N200
m a t r i x i , j = 0 / 1 matrix_{i,j} = 0/1 matrixi,j=0/1


題解:

常規解法:列舉長度,分別列舉位置,然後判斷邊界是否滿足,時間複雜度: O ( n 4 ) O(n^4) O(n4),那麼如何優化呢?

仔細思考,列舉長度和列舉位置是省不掉的,我們可以優化判斷邊界是否滿足,不用做四次迴圈挨個判斷。

具體有兩種優化方法,都是用到字首和思想:

優化一(一個額外陣列)

使用二維陣列儲存字首和。核心思想:假設從左往右列舉,當前長度為 k ,位置為 (i, j) ,那麼求邊界上的 1 的數量變成:sum1 - sum2 ,其中 sum1 是從 (i, j) 開始,長度為 k 的子陣列和,sum2 是比 sum1 小一層的子陣列和,即從 (i+1, j+1) 開始,長度為 k-2 的子陣列和,若 sum1 - sum2 == 4*k - 4,則是一個邊界為 1 的子陣列,在所有合法的情況中找最大值即可。

優化一程式碼:
#include <cstdio>
#include <algorithm>

using namespace std;

const int N = 201;

int a[N][N];
int n;

int main(void) {
    scanf("%d", &n);
    int ret = 0;
    for ( int i = 1; i <= n; ++i ) {
        for ( int j = 1; j <= n; ++j ) {
            scanf("%d", &a[i][j]);
            if ( a[i][j] && !ret ) ret = 1;
            a[i][j] += a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1];
        }
    }
    int x, y;
    for ( int k = 2; k <= n; ++k ) {
        for ( int i = 1; i + k - 1 <= n; ++i ) {
            for ( int j = 1; j + k - 1 <= n; ++j ) {
                x = i + k - 1;
                y = j + k - 1;
                int sum1 = a[x][y] + a[i - 1][j - 1];
                sum1 -= (a[x][j - 1] + a[i - 1][y]);
                if ( k == 2 && sum1 == 4 ) {
                    ret = max( ret, 2 );
                    continue;
                }
                if ( k > 2 ) {
                    int sum2 = a[x - 1][y - 1] + a[i][j];
                    sum2 -= ( a[x - 1][j] + a[i][y - 1] );
                    if ( sum1 - sum2 == 4 * k - 4 ) ret = max( ret, k );
                }
            }
        }
    }
    return 0 * printf("%d\n", ret);
}

優化二(兩個額外陣列)

使用兩個額外陣列,記錄每個位置往上最長多少個連續的1,往左最長多少個連續的1。在判斷邊界是否滿足條件時,直接通過預處理陣列判斷即可。

優化二程式碼:

#include <cstdio>
#include <algorithm>

using namespace std;

const int N = 200;

int n, val;
int r[N][N];
int c[N][N];

int main(void) {
    scanf("%d", &n);
    for ( int i = 0; i < n; ++i ) {
        for ( int j = 0; j < n; ++ j) {
            scanf("%d", &val);
            if ( !val ) continue;
            r[i][j] = !j ? 1 : r[i][j - 1] + 1;
            c[i][j] = !i ? 1 : c[i - 1][j] + 1; 
        }
    }
    int tmp, ret = 0;
    for ( int i = n - 1; i >= 0; --i ) {
        for ( int j = n - 1; j >= 0; --j ) {
            tmp = min( r[i][j], c[i][j] );
            while ( tmp > ret ) {
                if ( r[i - tmp + 1][j] >= tmp && c[i][j - tmp + 1] >= tmp ) {
                    ret = tmp;
                    break;
                }
                --tmp;
            }
        }
    }
    return 0 * printf("%d\n", ret);
}

法一空間小點,法二時間小點,自己選吧2333。。。

相關文章