悬线法,单调栈解决 HDU6957 Maximal submatrix

悬线法可以用来解决最大子矩阵等问题
模板题目: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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值