BZOJ1047 [HAOI2007]理想的正方形 单调队列
1047: [HAOI2007]理想的正方形
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 4184 Solved: 2361
[Submit][Status][Discuss]
Description
有一个a*b的整数组成的矩阵,现请你从中找出一个n*n的正方形区域,使得该区域所有数中的最大值和最小值
的差最小。
Input
第一行为3个整数,分别表示a,b,n的值第二行至第a+1行每行为b个非负整数,表示矩阵中相应位置上的数。每
行相邻两数之间用一空格分隔。
100%的数据2<=a,b<=1000,n<=a,n<=b,n<=1000
Output
仅一个整数,为a*b矩阵中所有“n*n正方形区域中的最大整数和最小整数的差值”的最小值。
Sample Input
5 4 2
1 2 5 6
0 17 16 0
16 17 2 1
2 10 2 1
1 2 2 2
1 2 5 6
0 17 16 0
16 17 2 1
2 10 2 1
1 2 2 2
Sample Output
1
题解:
在各行通过单调队列维护第i行中以j为结尾的长度为n的区间的最大值maxn和最小值minn
然后再在各列跑一边单调队列,求出所有n×n正方形区域的最大值和最小值之差
/************************************************************** Problem: 1047 User: mizersy Language: C++ Result: Accepted Time:1940 ms Memory:13144 kb ****************************************************************/ #include <bits/stdc++.h> using namespace std; const int N = 1005; struct Node{ int w,p; }; Node high[N],low[N]; int head1,head2,tail1,tail2; int maxn[N][N],minn[N][N]; int a[N][N]; int n,m,k; int main(){ scanf("%d%d%d",&n,&m,&k); for (int i = 1;i <= n;++i){ head1 = head2 = tail1 = tail2 = 0; for (int j = 1;j <= m;++j){ scanf("%d",&a[i][j]); while(head1 < tail1 && high[tail1-1].w < a[i][j]) tail1--; while(head1 < tail1 && j - high[head1].p >= k) head1++; high[tail1++] = Node{a[i][j],j}; if (j >= k) maxn[i][j] = high[head1].w; while(head2 < tail2 && low[tail2-1].w > a[i][j]) tail2--; while(head2 < tail2 && j - low[head2].p >= k)head2++; low[tail2++] = Node{a[i][j],j}; if (j >= k) minn[i][j] = low[head2].w; } } int ans = 1e9+7; for (int j = k;j <= m;++j){ head1 = head2 = tail1 = tail2 = 0; for (int i = 1;i <= n;++i){ while(head1 < tail1 && high[tail1-1].w < maxn[i][j]) tail1--; while(head1 < tail1 && i - high[head1].p >= k) head1++; high[tail1++] = Node{maxn[i][j],i}; while(head2 < tail2 && low[tail2-1].w > minn[i][j]) tail2--; while(head2 < tail2 && i - low[head2].p >= k) head2++; low[tail2++] = Node{minn[i][j],i}; if (j >= k && i >= k){ ans = min(ans,high[head1].w - low[head2].w); } } } printf("%d\n",ans); }