洛谷 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[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 }
posted @ 2017-10-19 18:15  hehe_54321  阅读(232)  评论(0编辑  收藏  举报
AmazingCounters.com