题意
在一个 a × b a\times b a×b 的矩阵中找到一个 n × n n\times n n×n 的正方形,使得正方形内的最大值和最小值之差最小。
思路
-
暴力
枚举每一个 n × n n\times n n×n 的正方形,枚举正方形内的最大和最小值。
时间复杂度: O ( a b n 2 ) O(abn^{2}) O(abn2) ,肯定会超时。
-
单调队列
要想优化上面这个算法,可以从“枚举正方形内的最大和最小值”入手。
对于 R M Q RMQ RMQ (这是一个链接)问题,我们可以用单调队列实现 O ( n ) O(n) O(n) 查找最大或最小值。
单调队列,是一种双端队列,即两头均可出队的队列。我们可以用 C + + C++ C++ S T L STL STL 库里自带的双端队列( d e q u e deque deque)实现。
头文件:
#include<queue>
声明:
deque <int> q;
从队尾入队:
q.push_back(x);
让队头出队:
q.pop_front();
让队尾出队:
q.pop_back();
返回队头的值:
q.front()
返回队尾的值:
q.back()
返回队列的长度:
q.size()
下面是查找每行区间最大值的代码实现:
//aa是行数,b是列数,n是区间大小 for (int i = 1;i <= aa;i ++)//a重复了,所以用了aa。。 { while (q.size()) q.pop_back();//清空队列 for (int j = 1;j <= b;j ++) { while (q.size() && j - q.front() + 1 > n) q.pop_front(); //保持队列长度不大于n while (q.size() && a[i][q.back()] <= a[i][j]) q.pop_back(); //如果队尾比第j个元素小,则它不可能是后面任何一个区间的最大值,故出队 q.push_back(j); //让j入队 m[i][j] = a[i][q.front()]; //m[i][j]表示第i行以j为末尾的区间最大值 } }
最小值也差不多。
我们用单调队列找出每一行各个区间的最大最小值,再用单调队列找出每一列为末尾时,最大最小值之差最小是多少即可。
完整代码
#include<iostream>
#include<queue>
using namespace std;
int aa,b,n;
int head,tail;
deque <int> q;
int a[1010][1010];
int m[1010][1010],f[1010][1010],w[1010][1010],v[1010][1010];
int res;
int main()
{
cin >> aa >> b >> n;
for (int i = 1;i <= aa;i ++)
for (int j = 1;j <= b;j ++)
cin >> a[i][j];
for (int i = 1;i <= aa;i ++)
{
while (q.size()) q.pop_back();
for (int j = 1;j <= b;j ++)
{
while (q.size() && j - q.front() + 1 > n) q.pop_front();
while (q.size() && a[i][q.back()] <= a[i][j]) q.pop_back();
q.push_back(j);
m[i][j] = a[i][q.front()];
}
}
for (int i = 1;i <= aa;i ++)
{
while (q.size()) q.pop_back();
for (int j = 1;j <= b;j ++)
{
while (q.size() && j - q.front() + 1 > n) q.pop_front();
while (q.size() && a[i][q.back()] >= a[i][j]) q.pop_back();
q.push_back(j);
f[i][j] = a[i][q.front()];
}
}
res = 0x7fffffff;
for (int j = 1;j <= b;j ++)
{
while (q.size()) q.pop_back();
for (int i = 1;i <= aa;i ++)
{
while (q.size() && i - q.front() + 1 > n) q.pop_front();
while (q.size() && m[i][j] >= m[q.back()][j])
q.pop_back();
q.push_back(i);
w[i][j] = m[q.front()][j];
//cout << w[i][j] << ' ';
}
//cout << endl;
}
for (int j = 1;j <= b;j ++)
{
while (q.size()) q.pop_back();
for (int i = 1;i <= aa;i ++)
{
while (q.size() && i - q.front() + 1 > n) q.pop_front();
while (q.size() && f[i][j] <= f[q.back()][j])
q.pop_back();
q.push_back(i);
v[i][j] = f[q.front()][j];
//cout << v[i][j] << ' ';
}
//cout << endl;
}
for (int i = n;i <= aa;i ++)
for (int j = n;j <= b;j ++)
res = min(res,w[i][j] - v[i][j]);
cout << res << endl;
}