洛谷 P2216 [HAOI2007]理想的正方形 || 二維RMQ的單調隊列
題目
這個題的算法核心就是求出以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[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 }
洛谷 P2216 [HAOI2007]理想的正方形 || 二維RMQ的單調隊列