这个题的算法核心就是求出以i,j为左上角,边长为n的矩阵中最小值和最大值。最小和最大值的求法类似。
单调队列做法:
以最小值为例:
q1[i][j]表示第i行上,从j列开始的n列的最小值。
$q1[i][j]=min(x[i][j],x[i][j+1],...,x[i][j+n-1])$
$q1[i][1]=min(x[i][1],x[i][2],...,x[i][n])$
$q1[i][2]=min(x[i][2],x[i][3],...,x[i][n+1])$
类似滑动窗口,因此直接枚举行,对于每一行单调队列处理即可。
q2[i][j]表示以第i行第j列为左上角的边长为n的矩阵的最小值
$q2[i][j]=min(q1[i][j],q1[i+1][j],...,q1[i+n-1][j])$
$q2[1][j]=min(q1[1][j],q1[2][j],...,q1[n][j])$
$q2[2][j]=min(q1[2][j],q1[3][j],...,q2[n+1][j])$
显然又可以用单调队列处理。总时间复杂度$O(n^2)$
(额外)更简短的做法(二维ST表,$O(n^2;log;n)$):
(由于要求的是正方形,因此可以$O(n^2;log;n)$,一般应该是$O({(n;log;n)}^2)$)
用f[i][j][k]来表示横纵坐标分别为i,j开始到i+2^k-1,j+2^k-1的整个矩阵(正方形)中的最大值
https://www.luogu.org/wiki/show?name=%E9%A2%98%E8%A7%A3+P2216
笔记:
1.这道题一开始想到了可能跟单调队列有关系,但是并没有想清楚。想了很久后,把q1和q2的关系式列了出来,一切就明朗了。
2.求最小值的单调队列队首(l)最小,队尾(r)最大,l<r。求最大值的单调队列队首(l)最大,队尾(r)最小,l<r。
求最小值的单调队列
2 3 5 4 6 1
1. 2
2. 2 3
3. 2 3 5
4. 2 3 4
5. 2 3 4 6
6. 1
3.单调队列的调试还算方便,只需要开着对单调队列的变量查看,观察内部元素是否正常就行了。
1 #include<cstdio> 2 #define min(a,b) ((a)>(b)?(b):(a)) 3 typedef long long LL; 4 LL a,b,n; 5 LL x[1010][1010]; 6 LL q1min[1010][1010],q1max[1010][1010],q2min[1010][1010],q2max[1010][1010]; 7 LL tmpmin[1010],tmpmax[1010],lmin,rmin,lmax,rmax,anss=0x3f3f3f3f3f3f3f3f; 8 int main() 9 { 10 LL i,j; 11 scanf("%lld%lld%lld",&a,&b,&n); 12 for(i=1;i<=a;i++) 13 for(j=1;j<=b;j++) 14 scanf("%lld",&x[i][j]); 15 for(i=1;i<=a;i++) 16 { 17 lmin=rmin=lmax=rmax=0; 18 for(j=1;j<=n;j++) 19 { 20 while(rmin>lmin&&x[i][tmpmin[rmin-1]]>=x[i][j]) rmin--; 21 tmpmin[rmin++]=j; 22 while(rmax>lmax&&x[i][tmpmax[rmax-1]]<=x[i][j]) rmax--; 23 tmpmax[rmax++]=j; 24 } 25 q1min[i][1]=x[i][tmpmin[lmin]]; 26 q1max[i][1]=x[i][tmpmax[lmax]]; 27 for(j=n+1;j<=b;j++) 28 { 29 if(rmin>lmin&&tmpmin[lmin]<=j-n) lmin++; 30 if(rmax>lmax&&tmpmax[lmax]<=j-n) lmax++; 31 while(rmin>lmin&&x[i][tmpmin[rmin-1]]>=x[i][j]) rmin--; 32 tmpmin[rmin++]=j; 33 while(rmax>lmax&&x[i][tmpmax[rmax-1]]<=x[i][j]) rmax--; 34 tmpmax[rmax++]=j; 35 q1min[i][j-n+1]=x[i][tmpmin[lmin]]; 36 q1max[i][j-n+1]=x[i][tmpmax[lmax]]; 37 } 38 } 39 for(i=1;i<=b;i++) 40 { 41 lmin=rmin=lmax=rmax=0; 42 for(j=1;j<=n;j++) 43 { 44 while(rmin>lmin&&q1min[tmpmin[rmin-1]][i]>=q1min[j][i]) rmin--; 45 tmpmin[rmin++]=j; 46 while(rmax>lmax&&q1max[tmpmax[rmax-1]][i]<=q1max[j][i]) rmax--; 47 tmpmax[rmax++]=j; 48 } 49 q2min[1][i]=q1min[tmpmin[lmin]][i]; 50 q2max[1][i]=q1max[tmpmax[lmax]][i]; 51 for(j=n+1;j<=a;j++) 52 { 53 if(rmin>lmin&&tmpmin[lmin]<=j-n) lmin++; 54 if(rmax>lmax&&tmpmax[lmax]<=j-n) lmax++; 55 while(rmin>lmin&&q1min[tmpmin[rmin-1]][i]>=q1min[j][i]) rmin--; 56 tmpmin[rmin++]=j; 57 while(rmax>lmax&&q1max[tmpmax[rmax-1]][i]<=q1max[j][i]) rmax--; 58 tmpmax[rmax++]=j; 59 q2min[j-n+1][i]=q1min[tmpmin[lmin]][i]; 60 q2max[j-n+1][i]=q1max[tmpmax[lmax]][i]; 61 } 62 } 63 for(i=1;i<=a-n+1;i++) 64 for(j=1;j<=b-n+1;j++) 65 anss=min(anss,q2max[i][j]-q2min[i][j]); 66 printf("%lld",anss); 67 return 0; 68 }