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; }