Tips:这主要是自己在秋招过程中的一些学习笔记。GoGoGo!
问题描述:一个m*n的矩阵(m>0,n>0)只有数字0或1组成,找到这个矩阵中只包含1的最大正方形,返回其面积。
解决方案一:前缀和
矩阵的前缀和sum[i][j]理解为左上角坐标[0][0],右下角[i][j]的矩形区域内的数值和。
若要求取左上角坐标[x][y],右下角[i][j]的矩形区域内的数值和tmp,则计算公式如下:
tmp = sum[i][j] - sum[i][j-y] - sum[i-x][j] + sum[x][y];
请看图解:
基于这个想法,求取最大的正方形就可以是,永远以右下角是坐标[i][j],边长从1遍历到min(i,j),去找到最大的填充一的正方形区域。代码如下:
#include <iostream>
#include <vector>
using namespace std;
int main() {
int m,n;
cin >> m >> n;
vector<vector<int>> matrix(m,vector<int>(n));
//为了简化sum的填充
//我们也可以先计算每一行的前缀和,再计算每一行形成的前缀和矩阵的列的前缀和
vector<vector<int>> sum(m+1,vector<int>(n+1,0));
for(int i = 0;i < m;++i) {
for(int j = 0;j < n;++j) {
cin >> matrix[i][j];
sum[i+1][j+1] = matrix[i][j] + sum[i][j+1] + sum[i+1][j] - sum[i][j];
}
}
int res = 0;
for(int i = 0;i <= m;++i) {
for(int j = 0;j <= n;++j) {
cout << sum[i][j] << " ";
}
cout << endl;
}
//想法是从右下角进行计算
for(int i = m-1;i >0;i--) {
for(int j = n - 1;j > 0;j--) {
for(int k = 1;k <= min(i+1,j+1);k++) {
int temp = sum[i + 1][j + 1] - sum[i + 1 - k][j + 1] - sum[i + 1][j + 1 - k] + sum[i + 1 - k][j + 1 - k];
if(temp == k*k)
res = max(k,res);
}
}
}
cout << res << endl;
}
输入:
4 4
1 0 1 0
1 0 1 1
1 1 1 1
0 1 0 1
输出:先打印的是前缀和矩阵,最后一行是边长
0 0 0 0 0
0 1 1 2 2
0 2 2 4 5
0 3 4 7 9
0 3 5 8 11
2
方案二:动态规划
dp矩阵是什么:dp[i][j]以 (i,j) 为右下角,且只包含 1 的正方形的边长最大值。注意一定要是包含右下角,说明matrix[i][j]是0,我们将不更新dp。
如何更新dp:dp的更新取决于[i][j]的[i][j-1],[i-1][j],[i-1][j-1]的值,如果他们中有一个是0,必然dp[i][j] = 1;如果他们之间最小是1,那么dp[i][j] = 2 ....因此,dp[i][j] = min(dp[i][j-1],dp[i-1][j],dp[i-1][j-1]);
代码:
int max_subMatrix(vector<vector<int>>& matrix) {
int m = matrix.size();
int n = matrix[0].size();
//以i,j为右下角的最大正方形边长
vector<vector<int>> dp(m+1,vector<int>(n+1,0));
int res = 0;
for(int i = 0;i < m;++i) {
for(int j = 0;j < n;++j) {
if(matrix[i][j] == 1) {
dp[i+1][j+1] = min(dp[i][j],min(dp[i][j+1],dp[i+1][j])) + 1;
}
res = max(res,dp[i+1][j+1]);
}
}
return res;
}