题干:
链接:https://ac.nowcoder.com/acm/contest/883/F
来源:牛客网
The semester is finally over and the summer holiday is coming. However, as part of your university's graduation requirement, you have to take part in some social service during the holiday. Eventually, you decided to join a volunteer group which will plant trees in a mountain.
To simplify the problem, let's represent the mountain where trees are to be planted with an N×NN \times NN×N grid. Let's number the rows 1\ 1 1 to N\ N N from top to bottom, and number the columns 1\ 1 1 to N\ N N from left to right. The elevation of the cell in the i\ i i-th row and j\ j j-th column is denoted by ai,ja_{i,j}ai,j. Your leader decides that trees should be planted in a rectangular area within the mountain and that the maximum difference in elevation among the cells in that rectangle should not exceed M. In other words, if the coordinates of the top-left and the bottom-right corners of the rectangle are (x1,y1)(x_1,y_1)(x1,y1) and (x2,y2)(x_2,y_2)(x2,y2), then the condition ∣ai,j−ak,l∣≤M|a_{i,j} - a_{k,l}| \le M∣ai,j−ak,l∣≤M must hold for x1≤i,k≤x2, y1≤j,l≤y2x_1 \le i,k \le x_2, \ y_1 \le j,l \le y_2x1≤i,k≤x2, y1≤j,l≤y2. Please help your leader calculate the maximum possible number of cells in such a rectangle so that he'll know how many trees will be planted.
输入描述:
The input contains multiple cases. The first line of the input contains a single integer T (1≤T≤1000)T \ (1 \le T \le 1000)T (1≤T≤1000), the number of cases.
For each case, the first line of the input contains two integers N (1≤N≤500)N\ (1 \le N \le 500)N (1≤N≤500) and M (0≤M≤105)M\ (0 \le M \le 10^5)M (0≤M≤105). The following N lines each contain N integers, where the j\ j j-th integer in the i\ i i-th line denotes ai,j (1≤ai,j≤105)a_{i,j} \ (1 \le a_{i,j} \le 10^5)ai,j (1≤ai,j≤105).
It is guaranteed that the sum of N3N^3N3 over all cases does not exceed 25⋅10725 \cdot 10^725⋅107.
输出描述:
For each case, print a single integer, the maximum number of cells in a valid rectangle.
示例1
输入
复制
2
2 0
1 2
2 1
3 1
1 3 2
2 3 1
3 2 1
输出
复制
1
4
题目大意:
给一个N*N矩阵(N<=500,且保证所有样例的,时限3秒)
找出最大的矩阵满足其中最大值和最小值的差小于等于M,让你输出的是这个可能的最大差值。
解题报告:
既然给你了N和时限这么清楚肯定就是让你找一个N^3的做法而不能带log。
首先枚举两行,然后剩下一个N用在求这一段中的最大最小值。
那么比较容易想到的就是二分长度然后拿个st表或者什么之类的扫一遍但是这样肯定带个log。
考虑尺取,这样需要在O1时间内求出当前窗口的最大值和最小值的差,选择单调队列。
但是这题用STL会被卡,,所以手写一波就OK了。
注意数组不是维护的前缀和,而是前缀最大值和前缀最小值。
AC代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define F first
#define S second
#define ll long long
#define pb push_back
#define pm make_pair
using namespace std;
typedef pair<int,int> PII;
const int MAX = 500 + 5;
int a[MAX][MAX],M,NUM[MAX],num[MAX];
int n;
int ans;
int qmin[555],qmax[555],f,b,F,B;
int main() {int t;scanf("%d",&t);while(t--) {scanf("%d%d",&n,&M);ans=0;for(int i = 1; i<=n; i++) {for(int j = 1; j<=n; j++) {scanf("%d",&a[i][j]);}}for(int u = 1; u<=n; u++) {for(int i = 1; i<=n; i++) num[i]=NUM[i]=a[u][i];for(int d = u; d<=n; d++) {for(int i = 1; i<=n; i++) num[i] = min(num[i],a[d][i]),NUM[i] = max(NUM[i],a[d][i]);
// deque<int> qmin; deque<int> qmax;f=F=1;b=B=1;int i = 1, j = 1;while(i <= n) {if(i > j) j = i;while(j <= n) {while(f < b && num[qmin[b-1]] >= num[j]) b--;qmin[b++]=j;while(F < B && NUM[qmax[B-1]] <= NUM[j]) B--;qmax[B++]=j;if(NUM[qmax[F]] - num[qmin[f]] > M) break;j++;}if(qmin[f] == i) f++;if(qmax[F] == i) F++;ans = max(ans,(j-i)*(d-u+1));i++;}}}printf("%d\n",ans);}return 0 ;
}
最近又重写了一个优先移动右端点的版本:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define FF first
#define SS second
#define ll long long
#define pb push_back
#define pm make_pair
using namespace std;
typedef pair<int,int> PII;
const int MAX = 500 + 5;
struct Deque {int a[MAX];int f,b;//f是队首 b是队尾Deque() {f=b=0;}void clear() {f=b=0;}bool empty() {return f==b;}void pop_back() {b--;}void pop_front(){f++;}void push_back(int x){a[b++] = x;}int back() {return a[b-1];}int front() {return a[f];}
}Max,Min;
int a[MAX][MAX],sum[MAX],SUM[MAX];
int n,M;
int main()
{int t;cin>>t;while(t--) {int ans = 0;Max.clear(),Min.clear();scanf("%d%d",&n,&M);for(int i = 1; i<=n; i++) {for(int j = 1; j<=n; j++) scanf("%d",&a[i][j]);}for(int up = 1; up<=n; up++) {for(int j = 1; j<=n; j++) sum[j] = SUM[j] = a[up][j];for(int down = up; down<=n; down++) {for(int j = 1; j<=n; j++) sum[j] = min(sum[j],a[down][j]),SUM[j] = max(SUM[j],a[down][j]);//预处理当前要用的sum和SUM数组int l = 1;Max.clear();Min.clear();for(int r = 1; r<=n; r++) {while(!Max.empty() && SUM[Max.back()] <= SUM[r]) Max.pop_back();Max.push_back(r);while(!Min.empty() && sum[Min.back()] >= sum[r]) Min.pop_back();Min.push_back(r);while(l<=r && SUM[Max.front()] - sum[Min.front()] > M) {if(Max.front() == l) Max.pop_front();if(Min.front() == l) Min.pop_front();l++;}ans = max(ans,(down-up+1) * (r-l+1));}}}printf("%d\n",ans);}return 0 ;
}