洛谷 P1387 最大正方形 二维前缀和与DP

P1387 最大正方形

P1387 最大正方形

题目描述

在一个n*m的只包含0和1的矩阵里找出一个不包含0的最大正方形,输出边长。

输入格式

输入文件第一行为两个整数n,m(1<=n,m<=100),接下来n行,每行m个数字,用空格隔开,0或1.

输出格式

一个整数,最大正方形的边长

输入输出样例

输入 #1复制

4 4
0 1 1 1
1 1 1 0
0 1 1 0
1 1 0 1

输出 #1复制

2

 一开始无从下手,不知道得到矩阵后怎么去判断正方形。

去翻看网上其他人的做法,大致就暴力+二维前缀和或者dp。

这里总结的做法是dp,也用到了前缀处理。

就放在这里做笔记。

思路():

1.准备两个数组,一个是记录某位置左边 left_count[ ][ ](不包括此位置)连续为 1 的个数,另一个同理,求此位置的上面 up_count[ ][ ]。这样就得到了可能的最大正方形边长 min(left_count[i][j - 1], up_count[i - 1][j]);

2. dp 求出每个位置,以这个位置为正方形的右下角,能构成的最大正方形的边长。

3.如果位置a[i][j] == 1,那么最大正方形边长应为 dp[i - 1][j - 1],min(left_count[i][j - 1], up_count[i - 1][j])中的最小值。

#include
#include

using namespace std;

int main()
{
    int n, m;
    scanf("%d %d", &n, &m);
    int a[105][105] = {0};
    int dp[105][105] = {0};
    int left_count[105][105] = {0};         // (i, j) 位置(不包括 (i, j) )左边连续的 1 的个数
    int up_count[105][105] = {0};           // (i, j) 位置(不包括 (i, j) )上面连续的 1 的个数
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            scanf("%d", &a[i][j]);
        }
    }
    // 左边前缀处理
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            if (a[i][j]) left_count[i][j]+= left_count[i][j - 1] + a[i][j];
            else left_count[i][j] = 0;
        }
    }
    // 上边前缀处理
    for (int j = 1; j <= m; j++)
    {
        for (int i = 1; i <= n; i++)
        {
            if (a[i][j]) up_count[i][j] = up_count[i - 1][j] + a[i][j];
            else up_count[i][j] = 0;
        }
    }
    int res = 0;
    // dp 求出每个位置,以这个位置为正方形的右下角,能构成的最大正方形的边长
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            if (a[i][j]) dp[i][j] = min(dp[i - 1][j - 1], min(left_count[i][j - 1], up_count[i - 1][j])) + 1, res = max(res, dp[i][j]);
        }
    }
    printf("%d\n", res);
    return 0;
}

 

你可能感兴趣的:(动态规划,算法)