PS:如果读过题了可以跳过题目描述直接到题解部分
提交链接:洛谷 P2216 [HAOI2007]理想的正方形
题目
题目描述
有一个 a × b a \times b a×b 的整数组成的矩阵,现请你从中找出一个 n × n n \times n n×n 的正方形区域,使得该区域所有数中的最大值和最小值的差最小。
输入格式
第一行为 3 3 3 个整数,分别表示 a , b , n a,b,n a,b,n 的值。
第二行至第 a + 1 a+1 a+1 行每行为 b b b 个非负整数,表示矩阵中相应位置上的数。每行相邻两数之间用一空格分隔。
输出格式
仅一个整数,为 a × b a \times b a×b 矩阵中所有“ n × n n \times n n×n 正方形区域中的最大整数和最小整数的差值”的最小值。
样例 #1
样例输入 #1
5 4 2
1 2 5 6
0 17 16 0
16 17 2 1
2 10 2 1
1 2 2 2
样例输出 #1
1
提示
问题规模。
矩阵中的所有数都不超过 1 , 000 , 000 , 000 1,000,000,000 1,000,000,000。
20 % 20\% 20% 的数据 2 ≤ a , b ≤ 100 , n ≤ a , n ≤ b , n ≤ 10 2 \le a,b \le 100,n \le a,n \le b,n \le 10 2≤a,b≤100,n≤a,n≤b,n≤10。
100 % 100\% 100% 的数据 2 ≤ a , b ≤ 1000 , n ≤ a , n ≤ b , n ≤ 100 2 \le a,b \le 1000,n \le a,n \le b,n \le 100 2≤a,b≤1000,n≤a,n≤b,n≤100。
题解
单调队列
用双向队列维护一个最远距离不超过n的单调队列。分别保存n个数中最大的和最小的。
再换个方向跑。
就可以得到每n*n个数中最大的和最小的。
最后遍历得出的数,找出最大值和最小值的差最小的就是答案了。
代码实现
//洛谷 P2216 [HAOI2007]理想的正方形
#pragma GCC optimize(3)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
int a,b,n;
int ans=0x7fffffff;//注意要给ans赋一个极大的初值!!!
deque<int> q;//这个的头文件书上写的是deque但好像用queue也可以
int c[1010][1010];
struct z{
int maxx;
int minn;
int ma;
int mi;
}d[1010][1010];
void in(int &x){
int nt;
x=0;
while(!isdigit(nt=getchar()));
x=nt^'0';
while(isdigit(nt=getchar())){
x=(x<<3)+(x<<1)+(nt^'0');
}
}
int main(){
register int i,j,k;
in(a),in(b),in(n);
for(i=1;i<=a;++i){
for(j=1;j<=b;++j){
in(c[i][j]);
}
}
for(i=1;i<=a;++i){
q.clear();
for(j=1;j<=b;++j){
while(q.size()&&j-q.front()+1>n){//最长距离不超过n
q.pop_front();
}
while(q.size()&&c[i][q.back()]<=c[i][j]){//单调
q.pop_back();
}
q.push_back(j);
d[i][j].maxx=c[i][q.front()];//保存
}
}
for(i=1;i<=a;++i){
q.clear();
for(j=1;j<=b;++j){
while(q.size()&&j-q.front()+1>n){
q.pop_front();
}
while(q.size()&&c[i][q.back()]>=c[i][j]){
q.pop_back();
}
q.push_back(j);
d[i][j].minn=c[i][q.front()];
}
}
for(j=1;j<=b;++j){
q.clear();
for(i=1;i<=a;++i){
while(q.size()&&i-q.front()+1>n){
q.pop_front();
}
while(q.size()&&d[q.back()][j].maxx<=d[i][j].maxx){
q.pop_back();
}
q.push_back(i);
d[i][j].ma=d[q.front()][j].maxx;
}
}
for(j=1;j<=b;++j){
q.clear();
for(i=1;i<=a;++i){
while(q.size()&&i-q.front()+1>n){
q.pop_front();
}
while(q.size()&&d[q.back()][j].minn>=d[i][j].minn){
q.pop_back();
}
q.push_back(i);
d[i][j].mi=d[q.front()][j].minn;
}
}
for(i=n;i<=a;++i){
for(j=n;j<=b;++j){
ans=min(ans,d[i][j].ma-d[i][j].mi);//遍历
}
}
printf("%d\n",ans);
return 0;
}