洛谷P1169 棋盘制作【悬线法】【区间dp】
题目:https://www.luogu.org/problemnew/show/P1169
题意:n*m的黑白格子,找到面积最大的黑白相间的正方形和矩形。
思路:传说中的悬线法!用下面这张图说明一下。
悬线法一般是用来求一个没有障碍点的最大子矩阵的。想象从上面垂下来好多的悬线,这些悬线被一个底所限制,并且可以左右移动但是也有范围限制。
现在某条悬线可以移动到的面积就是他能满足的子矩形的面积。比如我们已经处理好了$i-1$行,现在考虑$(i,j)$
对于这道题来说,如果$grid[i][j]!=grid[i-1][j]$就说明他们黑白颜色不同,那么这个以$i$行为底的悬线的高度就是$height[i-1][j]+1$
接下来我们考虑他的左右范围
首先我们可以需要预处理出每个位置可以到的左右范围,比如说$lft[i][j]$就是从$(i,j)$开始往左满足左右相间可以一直到第几列。
当我们要扩展一行的时候对于左边界只能取最右边的一个,对于右边界只能取最左边的。
1 #include<cstdio> 2 #include<cstdlib> 3 #include<map> 4 #include<set> 5 #include<cstring> 6 #include<algorithm> 7 #include<vector> 8 #include<cmath> 9 #include<stack> 10 #include<queue> 11 #include<iostream> 12 13 #define inf 0x3f3f3f3f 14 using namespace std; 15 typedef long long LL; 16 typedef pair<int, int> pr; 17 18 int n, m; 19 const int maxn = 2005; 20 int grid[maxn][maxn]; 21 int lft[maxn][maxn], rgt[maxn][maxn], height[maxn][maxn]; 22 23 int main() 24 { 25 scanf("%d%d", &n, &m); 26 for(int i = 1; i <= n; i++){ 27 for(int j = 1; j <= m; j++){ 28 scanf("%d", &grid[i][j]); 29 lft[i][j] = rgt[i][j] = j; 30 height[i][j] = 1; 31 } 32 } 33 34 for(int i = 1; i <= n; i++){ 35 for(int j = 2; j <= m; j++){ 36 if(grid[i][j] != grid[i][j - 1]){ 37 lft[i][j] = lft[i][j - 1]; 38 } 39 } 40 for(int j = m - 1; j > 0; j--){ 41 if(grid[i][j] != grid[i][j + 1]){ 42 rgt[i][j] = rgt[i][j + 1]; 43 } 44 } 45 } 46 47 int anssqu = 0, ansrec = 0; 48 for(int i = 1; i <= n; i++){ 49 for(int j = 1; j <= m; j++){ 50 if(i > 1 && grid[i][j] != grid[i - 1][j]){ 51 lft[i][j] = max(lft[i][j], lft[i - 1][j]); 52 rgt[i][j] = min(rgt[i][j], rgt[i - 1][j]); 53 height[i][j] = height[i - 1][j] + 1; 54 } 55 int row = rgt[i][j] - lft[i][j] + 1; 56 int col = min(row, height[i][j]); 57 anssqu = max(anssqu, col * col); 58 ansrec = max(ansrec, row * height[i][j]); 59 } 60 } 61 62 printf("%d\n%d\n", anssqu, ansrec); 63 }