​原题跳转​

​题解参考某佬做法​

单调栈

#include<iostream>
#include<string.h>

using namespace std;

typedef long long ll;

// 原矩阵
int arr[2005][2005], n, m;

// mx记录当前行,当前列的非递减个数,看下面mx数组求解的代码就可以理解了。。。
// s是栈数组
// w记录宽度。。。。
ll mx[2005], s[2005], w[2005];

int main(){

int t;

scanf("%d", &t);

while(t--){

scanf("%d%d", &n, &m);
memset(mx, 0, sizeof(mx));

for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
scanf("%d", &arr[i][j]);
}
}

ll ans = 0;

for(int i = 1; i <= n; i++){
// 求出当前行的mx数组
for(int j = 1; j <= m; j++){
if(arr[i][j] >= arr[i - 1][j]){
mx[j]++;
}else{
mx[j] = 1;
}
}

// mx[1 ~ j]从中选一个连续的区间l, r, 设区间最小值为min, 使得min * (r - l + 1)最大
// 单调栈
int top;
mx[m + 1] = top = 0;

s[0] = 0; // 栈数组
w[0] = 0; // 权重数组,记录宽度

// m+1, 是为了让最后结算一次
for(int j = 1; j <= m + 1; j++){
if(mx[j] >= s[top]){

s[++top] = mx[j];
w[top] = 1;
} else{

int width = 0;
while(s[top] > mx[j]){
width += w[top];
ans = max(ans, width * s[top]);
// 弹出栈中大于当前mx[j]的,维护一个非递减序列
top--;
}
s[++top] = mx[j], w[top] = width + 1;
}
}
}

printf("%lld\n", ans);

}
return 0;
}

悬线法

​悬线法详解​

2021“MINIEYE杯”中国大学生算法设计超级联赛(1)- 1008(Maximal submatrix)_单调栈

​悬线法板子​

#include<iostream>

using namespace std;

// mx数组代表当前i位置的高度
// L数组代表以当前i位置高度为矩形的长方形向左扩展最多能扩展到那个位置
// R数组代表以当前i位置高度为矩形的长方形向右扩展最多能扩展到那个位置
int mx[2005], L[2005], R[2005], n;

int main(){

long long ans = 0;

for (int i = 1; i <= n; ++i) {
scanf("%d", &mx[i]);
L[i] = R[i] = i;
}

for (int i = 1; i <= n; i++) {
while (L[i] > 1 && mx[i] <= mx[L[i] - 1]){
L[i] = L[L[i] - 1];
}
}

for (int i = n; i >= 1; i--) {
while (R[i] < n && mx[i] <= mx[R[i] + 1]){
R[i] = R[R[i] + 1];
}
}

for (int i = 1; i <= n; i++) {
ans = max(ans, (long long)(R[i] - L[i] + 1) * mx[i]);
}

return 0;
}

​AC code​

#include<iostream>
#include<string.h>

using namespace std;

typedef long long ll;

int arr[2005][2005], n, m;

ll mx[2005], L[2005], R[2005];

int main(){

int t;

scanf("%d", &t);

while(t--){

scanf("%d%d", &n, &m);
memset(mx, 0, sizeof(mx));

for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
scanf("%d", &arr[i][j]);
}
}

ll ans = 0;

for(int i = 1; i <= n; i++){
// 求出当前行的mx数组
for(int j = 1; j <= m; j++){
L[j] = R[j] = j;
if(arr[i][j] >= arr[i - 1][j]){
mx[j]++;
}else{
mx[j] = 1;
}
}
for (int j = 1; j <= m; ++j) {
while (L[j] > 1 && mx[j] <= mx[L[j] - 1]){
L[j] = L[L[j] - 1];
}
}

for (int j = m; j >= 1; j--) {
while(R[j] < m && mx[j] <= mx[R[j] + 1]){
R[j] = R[R[j] + 1];
}
}

for (int j = 1; j <= n; ++j) {
ans = max(ans, (ll)((R[j] - L[j] + 1) * mx[j]));
}
}

printf("%lld\n", ans);

}
return 0;
}