1. 程式人生 > >洛谷 P2216 [HAOI2007]理想的正方形 || 二維RMQ的單調隊列

洛谷 P2216 [HAOI2007]理想的正方形 || 二維RMQ的單調隊列

pmi 最小值 typedef 關系 pan 算法 一行 ++ min

題目

這個題的算法核心就是求出以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 }

洛谷 P2216 [HAOI2007]理想的正方形 || 二維RMQ的單調隊列