【洛谷习题】最大正方形
题目链接:https://www.luogu.org/problemnew/show/P1387
最近学OI有点过火,今早上愣是没睡醒,上午的自习就没有了!不过还是很开心,连A三道DP水题。
这个题的话,思路比较清晰。我们想知道有没有边长为l的正方形,肯定要先知道有没有边长为l-1的正方形。我们枚举一个正方形的左上角,然后搬着一个边长为l-1的正方形在四个角上跑,如果这四个角上的小正方形全是1,那么这个边长为l的正方形肯定也全是1,否则取其中最大的。描述的有点凌乱,看代码就好了。一开始傻了一般开了一个四维数组保存正方形的四个顶点,这样是没有必要的,因为一个左上角的顶点和边长就能确定一个正方形。
1 #include <cstdio> 2 #include <algorithm> 3 4 using namespace std; 5 6 const int mmax = 105; 7 8 int map[mmax][mmax], dp[mmax][mmax][mmax]; 9 int main() { 10 int n, m, ans = 1; 11 scanf("%d%d", &n, &m); 12 for (int i = 1; i <= n; ++i) 13 for (int j = 1; j <= m; ++j) { 14 scanf("%d", &map[i][j]); 15 if (map[i][j]) dp[i][j][1] = 1; 16 } 17 for (int l = 2; l <= min(n,m); ++l) { 18 for (int i = 1; i <= n - l + 1; ++i) 19 for (int j = 1; j <= m - l + 1; ++j) { 20 if (dp[i][j][l - 1] == l - 1 && dp[i][j + 1][l - 1] == l - 1 && dp[i + 1][j][l - 1] == l - 1 && dp[i + 1][j + 1][l - 1] == l - 1) 21 dp[i][j][l] = l; 22 else dp[i][j][l] = max(dp[i][j][l - 1], max(dp[i][j + 1][l - 1], max(dp[i + 1][j][l - 1], dp[i + 1][j + 1][l - 1]))); 23 ans = max(ans, dp[i][j][l]); 24 } 25 if (ans < l) break; 26 } 27 printf("%d", ans); 28 return 0; 29 }
实际上,这样做也不算太优秀。我们可以定义dp[i][j]表示以(i,j)为右下角的最大正方形的边长。那么对于map[i][j]==1,则有dp[i][j]=min(dp[i-1][j-1],min(dp[i-1][j],dp[i][j-1]))+1。显然是对的,每次考虑能否将(i,j)加入到已有的正方形中,那他一定可以加入以(i-1,j-1)、(i-1,j)、(i,j-1)为右下角的最大正方形中最小的那个,因为最大正方形要求里面全是1。
1 #include <cstdio> 2 #include <algorithm> 3 4 using namespace std; 5 6 const int mmax = 105; 7 8 int dp[mmax][mmax]; 9 10 int main() { 11 int n, m, point, ans = 0; 12 scanf("%d%d", &n, &m); 13 for (int i = 1; i <= n; ++i) 14 for (int j = 1; j <= m; ++j) { 15 scanf("%d", &point); 16 if (point) { 17 dp[i][j] = min(dp[i - 1][j - 1], min(dp[i - 1][j], dp[i][j - 1])) + 1; 18 ans = max(ans, dp[i][j]); 19 } 20 } 21 printf("%d", ans); 22 return 0; 23 }