P1387 最大正方形
输入输出样例
输入 #1复制
4 4
0 1 1 1
1 1 1 0
0 1 1 0
1 1 0 1
输出 #1复制
2
总结目录
1.状态方程的获取
2.初始条件与计算顺序
1 状态方程获取
这题思维难度不高,关键在于如何定义dp。对于这种类似于棋盘的图形dp问题,显然属于多维dp了。但是dp[i][j]到底应该如何进行具体的定义。
一种是直接根据题目进行修改的:定义dp[i][j]为一直到[i,j]区间能够得到的最大正方形的边长。这样的话我们最后求的就是dp[m][n],但是这样子一方面全局解和局部最优解之间关系并不明确,因为局部最优解得到的最大正方形并不一定包含[i][j](即[i][j]不一定是1,最大正方形可能是一个更小的结构)
因此,这里的图形dp的定义为:以[i][j]为正方形右下角点的最大边长,这个思想其实有一点像3种遍历中的以num[i]为结尾的最大升序列/最大子列和。以后有关图形的dp问题也可以考虑从图形的某个角点出发进行定义dp[i][j]
那么很简单就能写出
if(mat[i][j]==1)//当前角点为1才有资格作为正方形右下角点
dp[i][j]=min{ dp[i-1][j],dp[i-1][j-1],dp[i][j-1] }+1;
2 初始条件与计算顺序
0行与0列记为0,表明这里无法作为正方形角点。计算顺序为从左到右,从上到下。
代码
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int mat[105][105];
int dp[105][105];//dp[i][j]定义为以[i,j]为正方形右下角(包含[i,j])的最大能够构成的正方形的边数
int main() {
memset(mat, 0, sizeof(mat));
memset(dp, 0, sizeof(dp));
int m, n;
cin >> m >> n;
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
cin >> mat[i][j];
}
}
int res = 0;
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (mat[i][j] == 1) {
dp[i][j] = min({ dp[i - 1][j],dp[i][j - 1],dp[i - 1][j - 1] }) + 1;
}
res = max(res, dp[i][j]);
}
}
cout << res;
return 0;
}