题意简述
二维滑动窗口。给定一个 a × b a\times b a×b的矩阵,和一个大小为 k × k k\times k k×k的滑动窗口,求这个窗口在滑动过程中所看到的(最大值-最小值)最小能到多少。
数据
输入
5 4 2
1 2 5 6
0 17 16 0
16 17 2 1
2 10 2 1
1 2 2 2
输出
1
思路
理论上是珂以跑两维单调队列的,当然也珂以跑二维
S
T
ST
ST表。由于我不会二维,所以我只能
S
T
ST
ST表套单调队列来写(群众:wdnmd您是沙雕么???)
好的我们来看看我的瞎搞做法。。。
设身处地的想一下,如果你不会二维的单调队列或
S
T
ST
ST表,你会怎么办?
首先要先枚举区间左端点
i
i
i,然后把所有行上从
i
i
i到
i
+
x
−
1
i+x-1
i+x−1之间的最大/最小求出来(所以这告诉我们,我们对每一行都要写一个
S
T
ST
ST表)。如下图:
蓝色部分就是我们按行处理的最大/最小值。我们把每一行的最大/最小值放到数组里面,然后就变成一维的问题了。如下图:
对保存行最大值那个数组(设为
t
m
p
x
tmpx
tmpx),我们跑一遍维护最大值的单调队列。对保存行最小值的那个数组(设为
t
m
p
n
tmpn
tmpn),我们跑一遍维护最小值的单调队列,取两个队首相减的最小值即可求出答案。
代码(有很多注释):
#include<bits/stdc++.h>
#define N 1010
#define LogN 11
#define K 110
using namespace std;
int mp[N][N];
int n,m,x;
void Input()//读入没什么好说的。。。
{
scanf("%d%d%d",&n,&m,&x);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&mp[i][j]);
}
}
}
//以下变量名中,以x结尾的是求最大用的,以n结尾的是求最小用的
//比如stx就是最大值的st表,stn就是最小值的st表
int stx[N][N][LogN],stn[N][N][LogN],lg[N];
int tmpx[N],tmpn[N];
void BuildST()
{
for(int i=1;i<=n;i++)//预处理每个数的log2值,常数小一点
{
lg[i]=lg[i-1]+((1<<(lg[i-1]+1))==i);
}
for(int i=1;i<=n;i++)//ST表的边界(这个少了药丸啊。。。)
{
for(int j=1;j<=m;j++)
{
stn[i][j][0]=stx[i][j][0]=mp[i][j];
}
}
for(int i=1;i<=n;i++)//转移
{
for(int k=1;(1<<k)<=m;k++)
{
for(int j=1;j+(1<<(k-1))<=m;j++)
{
stx[i][j][k]=max(stx[i][j][k-1],stx[i][j+(1<<(k-1))][k-1]);
stn[i][j][k]=min(stn[i][j][k-1],stn[i][j+(1<<(k-1))][k-1]);
}
}
}
}
int Query(int st[][N][LogN],int i,int l,int r)//求最大/最小
//第一个参数是一个指针,是stn就表示询问最小,是stx就表示询问最大
{
int lc=lg[x];//r-l+1==x
if (st==stx)//询问最大
{
return max(st[i][l][lc],st[i][r-(1<<lc)+1][lc]);
}
else//询问最小
{
return min(st[i][l][lc],st[i][r-(1<<lc)+1][lc]);
}
}
int Qx[N],hx,tx;int Qn[N],hn,tn;
void Solve()
{
int totans=0x3f3f3f3f;//总共的答案
for(int i=1;i+x-1<=m;i++)
{
memset(tmpn,0,sizeof(tmpn));
memset(tmpx,0,sizeof(tmpx));
for(int j=1;j<=n;j++)//上图的蓝色部分(降维打击)
{
tmpx[j]=Query(stx,j,i,i+x-1);
tmpn[j]=Query(stn,j,i,i+x-1);
}
//以下是洛谷1886滑动窗口的代码
hx=tx=hn=tn=1;
for(int j=1;j<=x;j++)
{
while(hx<tx and tmpx[Qx[tx-1]]<=tmpx[j]) tx--;
Qx[tx]=j,tx++;
while(hn<tn and tmpn[Qn[tn-1]]>=tmpn[j]) tn--;
Qn[tn]=j,tn++;
}
int ans=tmpx[Qx[hx]]-tmpn[Qn[hn]];
for(int j=x+1;j<=n;j++)
{
while(hx<tx and tmpx[Qx[tx-1]]<=tmpx[j]) tx--;
Qx[tx]=j,tx++;
while(hn<tn and tmpn[Qn[tn-1]]>=tmpn[j]) tn--;
Qn[tn]=j,tn++;
if (j-Qx[hx]+1>x)
{
++hx;
}
if (j-Qn[hn]+1>x)
{
++hn;
}
//ans记录以i为左端点的蓝色部分最大-最小的最小值
ans=min(ans,tmpx[Qx[hx]]-tmpn[Qn[hn]]);
}
totans=min(totans,ans);//计算到总答案
}
printf("%d\n",totans);
}
main()
{
Input();
BuildST();
Solve();
return 0;
}