问题描述
现有 H × W H×W H×W个边长为1cm的正方形瓷砖排列在一起,其中有一部分瓷砖沾有污迹,求仅由干净瓷砖构成的最大正方形的面积。
输入:
H
H
H
W
W
W
c
1
,
1
c_{1,1}
c1,1
c
1
,
2
c_{1,2}
c1,2 …
c
1
,
W
c_{1,W}
c1,W
c
2
,
1
c_{2,1}
c2,1
c
2
,
2
c_{2,2}
c2,2 …
c
2
,
W
c_{2,W}
c2,W
…
c
H
,
1
c_{H,1}
cH,1
c
H
,
2
c_{H,2}
cH,2 …
c
H
,
W
c_{H,W}
cH,W
第1行输入2个整数H、W,用空格隔开。接下来H行输入
H
×
W
H×W
H×W个代表瓷砖的整数
c
i
j
c_{ij}
cij。为1表示瓷砖有污渍,为0表示干净
输出:
输出面积的最大值,占1行
限制:
1 ≤ H,W ≤ 1400
输入示例
4 5
0 0 1 0 0
1 0 0 0 0
0 0 0 1 0
0 0 0 1 0
输出示例
4
讲解
首先,本题最容易想到的方法就是检查所有正方形内是否包含1,复杂度为 O ( H W × m i n ( H , W ) 3 ) O(HW×min(H,W)^3) O(HW×min(H,W)3)。
如果用动态规划法求解,可将复杂度控制在 O ( H W ) O(HW) O(HW)。设存储小规模局部问题的内存空间(变量)为dp[H][W],dp[i][j]中存储着从瓷砖(i,j)向左上方扩展可形成的最大正方形的边长(瓷砖数)。
dp[i][j]的值等于其左上、上方、左侧元素中最小的值加1。
从下面的伪代码中可以看出,各行的处理从左侧向右侧进行,整体如同自上而下的逐行扫描。这样一来,下面一行在计算dp[i][j]时,其左上、上方、左侧的元素值都已经计算完毕,可以直接使用
用动态规划法求最大正方形:
for i = 1 to H-1
for j = 1 to W-1
如果G[i][j]沾有污渍
dp[i][j] = 0
else
dp[i][j] = min(dp[i-1][j-1], min(dp[i-1][j], dp[i][j-1])) + 1
maxWidth = max(maxWidth, dp[i][j])
AC代码如下
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAX 1400
int dp[MAX][MAX], G[MAX][MAX];
int getLargestSquare(int H, int W) {
int maxWidth = 0;
for(int i = 0; i < H; i++) {
for(int j = 0; j < W; j++) {
dp[i][j] = (G[i][j] + 1) % 2;
maxWidth |= dp[i][j];
}
}
for(int i = 1; i < H; i++) {
for(int j = 1; j < W; j++) {
if(G[i][j]) {
dp[i][j] = 0;
} else {
dp[i][j] = min(dp[i-1][j-1], min(dp[i-1][j], dp[i][j-1]) ) + 1;
maxWidth = max(maxWidth, dp[i][j]);
}
}
}
return maxWidth * maxWidth;
}
int main(){
int H, W;
scanf("%d %d", &H, &W);
for(int i = 0; i < H; i++) {
for(int j = 0; j < W; j++) scanf("%d", &G[i][j]);
}
printf("%d\n", getLargestSquare(H, W));
return 0;
}