洛谷1169:棋盘制作(悬线法)
悬线法:
用于解决给定矩阵满足条件的最大子矩阵。
做法:
用一条线(横竖都行)左右移动直到不满足约束条件或者到达边界。
定义:
\(left(i,j)\):表示\((i,j)\)向左扩展能够到达的最左边的位置。
\(right(i,j)\):表示\((i,j)\)向右扩展能够到达的最右边的位置。
\(up(i,j)\):表示\((i,j)\)向上能够拓展的最长长度。
递推公式:
\(left(i,j)=max\{left(i,j),left(i-1,j)\}\).
\(right(i,j)=min(right(i,j),right(i-1,j))\).
因为有\(up\)数组,\(up\)数组表示向上拓展的最长长度,所以要考虑上一层的情况。
#include
using namespace std;
const int maxn = 2e3 + 10;
int a[maxn][maxn], n, m;
int lef[maxn][maxn];
int rig[maxn][maxn];
int up[maxn][maxn];
int main()
{
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
{
scanf("%d", &a[i][j]);
lef[i][j] = rig[i][j] = j;
up[i][j] = 1;
}
for(int i = 1; i <= n; i++)
{
for(int j = 2; j <= m; j++)
if(a[i][j] != a[i][j-1])
lef[i][j] = lef[i][j-1];
}
for(int i = 1; i <= n; i++)
{
for(int j = m-1; j >= 1; j--)
if(a[i][j] != a[i][j+1])
rig[i][j] = rig[i][j+1];
}
int ans1, ans2;
ans1 = ans2 = 0;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
{
if(i > 1 && a[i][j] != a[i-1][j])
{
lef[i][j] = max(lef[i][j], lef[i-1][j]);
rig[i][j] = min(rig[i][j], rig[i-1][j]);
up[i][j] = up[i-1][j]+1;
}
int a = rig[i][j] - lef[i][j] + 1;
int b = up[i][j];
ans1 = max(ans1, min(a,b)*min(a,b));//Õý·½ÐÎ
ans2 = max(ans2, a*b);
} cout << ans1 << endl << ans2 << endl;
return 0;
}