悬线法可以用来解决最大子矩阵等问题
模板题目:https://www.luogu.com.cn/problem/SP1805
大意:在一条水平线上有n个宽为1的矩形,求包含于这些矩形的最大子矩形面积。
代码
/*
l[i]:为当前位置i的悬线能扩展到的最左边的位置,初始为i ,我们需要进一步判断还能不能进一步往左扩展。
如果当前l[i]==1 ,则已经扩展到了边界,不可以。
如果当前a[i] > a[l[i] - 1],则从当前悬线扩展到的位置不能再往左扩展了。
如果当前a[i] <= a[l[i] - 1]则从当前悬线还可以往左扩展,并且l[i]-1位置的悬线能向左扩展到的位置,
i位置的悬线一定也可以扩展到,于是我们将l[i]更新为l[l[i]-1],并继续执行判断。
*/
#include <algorithm>
#include <cstdio>
using std::max;
const int N = 100010;
int n, a[N];
int l[N], r[N];
long long ans;
int main() {
while (scanf("%d", &n) != EOF && n) {
ans = 0;
for (int i = 1; i <= n; i++) scanf("%d", &a[i]), l[i] = r[i] = i;
for (int i = 1; i <= n; i++)
while (l[i] > 1 && a[i] <= a[l[i] - 1]) l[i] = l[l[i] - 1];
for (int i = n; i >= 1; i--)
while (r[i] < n && a[i] <= a[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) * a[i]);
printf("%lld\n", ans);
}
return 0;
}
题目 : HDU6957 Maximal submatrix
悬线法
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N = 2e3+5;
int g[N][N],h[N][N],l[N],r[N];
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin>>t;
while(t--){
ll n,m,ans=0;
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>g[i][j];
if(g[i][j]>=g[i-1][j]) h[i][j]=h[i-1][j]+1;
else h[i][j]=1;
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
l[j]=r[j]=j;
while(l[j]>1 && h[i][j]<=h[i][l[j]-1]) l[j]=l[l[j]-1];
}
for(int j=m;j;j--){
while(r[j]<m && h[i][j]<=h[i][r[j]+1]) r[j]=r[r[j]+1];
}
for(int j=1;j<=m;j++) ans=max(ans,(ll)(h[i][j]*(r[j]-l[j]+1)));
}
cout<<ans<<endl;
}
return 0;
}
单调栈
用单调栈维护,可以知道,左边第一个比当前位置为i的值小的数的数值和位置j,用这一个性质,i-j 就是当前值往左边扩展的距离。
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N = 2e3+5;
int g[N][N],h[N][N],q[N],l[N],r[N];
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin>>t;
while(t--){
ll n,m,ans=0;
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>g[i][j];
if(g[i][j]>=g[i-1][j]) h[i][j]=h[i-1][j]+1;
else h[i][j]=1;
}
}
int tt=0;
for(int i=1;i<=n;i++){
tt=0;q[tt]=0;
for(int j=1;j<=m;j++){
while(tt && h[i][q[tt]]>=h[i][j]) tt--;//找到左边第一个比当前值小的坐标
l[j]=j-q[tt];
q[++tt]=j;
}
tt=0;
q[tt]=m+1;
for(int j=m;j;j--){
while(tt && h[i][q[tt]]>=h[i][j]) tt--;
r[j]=q[tt]-j;
q[++tt]=j;
}
for(int j=1;j<=m;j++) ans=max(ans,(ll)(h[i][j]*(r[j]+l[j]-1)));
}
cout<<ans<<endl;
}
return 0;
}