A题:变形的扫描法的典型题。
比赛的时候我一看这题就想到了上次做的LA3029,不过那题做的太久了,没有什么印象了,最后悲剧的没有A出来。
先讲下LA3029:点击打开链接
那题是给你一个矩阵,每个格子满或者空,求最大的空的子矩阵。
这题是求满足每行和每列都是等差数列的最大子矩阵。其实做法都差不多,都是朴素的O(m^2*n^2),扫描法简化后O(m*n)的做法。
要注意的地方:
1.维护left,right时要不断更新两个标记量,lo(当前最左的运动极限,列标号表示),ld(当前的从左向右的公差),ro,rd同理。
第一行的时候:不受到上面的行影响,所以得特判。
a[i][j]-a[i][j-1]==ld,left[i][j]=lo,否则ld=a[i][j]-a[i][j-1],lo=j-1,left[i][j]=lo;
不是第一行的时候:
left[i][j]=max(lo,left[i-1][j]);
第一列和第二列也得特判,因为最小要两项才会有公差。
维护right同理。
2.维护up数组,只要求长度就行了,维护ud(列上的公差)。
第一行和第二行:
up[i][j]=i+1;
其他行:
a[i][j]-a[i-1][j]==ud, up[i][j]=up[i-1][j]+1;
否则:up[i][j]=2;ud=a[i][j]-a[i-1][j];
3.特别注意一下,最终子矩阵的表示方法:
不是(right[i][j]-left[i][j]+1)*up[i][j];
必须先判断一下左边和右边是否是同一个等差数列:a[i][j]-a[i][j+1]==a[i][j-1]-a[i][j]
代码:
#include<iostream>
#include<cstdio>
#include<vector>
#include<string>
#include<queue>
#include<cmath>
#include<algorithm>
#include<cstring>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define maxn 3005
#define INF 0xfffffff
#define mem(a,b) memset(a,b,sizeof(a))
#define FOR(i,s,t) for(int i=s;i<=t;i++)
#define ull unsigned long long
#define ll long long
using namespace std;
int a[maxn][maxn],Left[maxn][maxn],Right[maxn][maxn],up[maxn][maxn];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=0; i<n; i++)
{
for(int j=0; j<m; j++)
{
scanf("%d",&a[i][j]);
}
}
//维护Left,Right,up
int lo,ld;
for(int i=0; i<n; i++)
{
lo=0,ld=a[i][1]-a[i][0];
for(int j=0; j<m; j++)
{
if(i==0)//第一行
{
if(j==0||j==1)
{
Left[i][j]=lo;
}
else
{
if(a[i][j]-a[i][j-1]==ld)
{
Left[i][j]=lo;
}
else
{
lo=j-1;
ld=a[i][j]-a[i][j-1];
Left[i][j]=lo;
}
}
}
else//不是第一行
{
if(j==0||j==1)
{
Left[i][j]=max(lo,Left[i-1][j]);
}
else
{
if(a[i][j]-a[i][j-1]==ld)
{
Left[i][j]=max(lo,Left[i-1][j]);
}
else
{
lo=j-1;
ld=a[i][j]-a[i][j-1];
Left[i][j]=max(lo,Left[i-1][j]);
}
}
}
}
}
//维护Right
int ro,rd;
for(int i=0; i<n; i++)
{
ro=m-1,rd=a[i][m-2]-a[i][m-1];
for(int j=m-1; j>=0; j--)
{
if(i==0)//第一行
{
if(j==m-1||j==m-2)
{
Right[i][j]=ro;
}
else
{
if(a[i][j]-a[i][j+1]==rd)
{
Right[i][j]=min(ro,Right[i-1][j]);
}
else
{
rd=a[i][j]-a[i][j+1];
ro=j+1;
Right[i][j]=min(ro,Right[i-1][j]);
}
}
}
else//不是第一行
{
if(j==m-1||j==m-2)
{
Right[i][j]=min(ro,Right[i-1][j]);
}
else
{
if(a[i][j]-a[i][j+1]==rd)
{
Right[i][j]=min(ro,Right[i-1][j]);
}
else
{
rd=a[i][j]-a[i][j+1];
ro=j+1;
Right[i][j]=min(ro,Right[i-1][j]);
}
}
}
}
}
//维护up
int ud,uo;
for(int j=0;j<m;j++)//从每一列开始
{
ud=a[1][j]-a[0][j];
for(int i=0;i<n;i++)
{
if(i==0||i==1)
{
up[i][j]=i+1;
}
else
{
if(a[i][j]-a[i-1][j]==ud)
{
up[i][j]=up[i-1][j]+1;
}
else
{
ud=a[i][j]-a[i-1][j];
up[i][j]=2;
}
}
}
}
int ans=0;
int tt=1;
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
int tmp;
int tmp1=(j-Left[i][j]+1)*up[i][j];
int tmp2=(Right[i][j]-j+1)*up[i][j];
tmp=max(tmp1,tmp2);
if(a[i][j+1]-a[i][j]==a[i][j]-a[i][j-1])
{
int tmp3=(Right[i][j]-Left[i][j]+1)*up[i][j];
tmp=max(tmp,tmp3);
}
if(tmp>ans)
{
ans=tmp;
}
}
}
printf("%d\n",ans);
}
return 0;
}