蓝桥杯 - 第十三届省赛真题-最大子矩阵(线段树 + 双指针)

链接:第十三届省赛真题-最大子矩阵

题意:
在这里插入图片描述

思路:
枚举上下边界,将这几行看看成一行,然后就可以双指针跑一遍求最大的合法区间了。最大最小值用线段树或rmq维护即可。复杂度大概 o ( n 3 m l o g m ) o(n ^ 3mlogm) o(n3mlogm),如果把几行看成一行的过程也用线段树优化一下复杂度就可以是 o ( n 2 ( l o g n ) m ( l o g m ) ) o(n ^ 2(logn)m(logm)) o(n2(logn)m(logm)).

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 7;
typedef long long ll;
const int mod = 998244353;
int a[101][maxn] , nowa[maxn] , nowi[maxn], n , m , k;
int ma[maxn << 2],mi[maxn << 2];
void build(int l , int r,int rt){
    if(l == r){
        ma[rt] = nowa[l];
        mi[rt] = nowi[l];
        return ;
    }
    int mid = (l + r) / 2;
    build(l , mid , rt << 1);
    build(mid + 1 , r , rt << 1 | 1);
    ma[rt] = max(ma[rt << 1] , ma[rt << 1 | 1]);
    mi[rt] = min(mi[rt << 1] , mi[rt << 1 | 1]);

}
int queryma(int L,int R,int l , int r,int rt){
    int res = 0;
    if(L <= l && R >= r){
        return ma[rt];
    }
    int mid = (l + r) / 2;
    if(L <= mid) res = max(res , queryma(L , R , l , mid , rt << 1));
    if(R > mid) res = max(res , queryma(L , R , mid + 1 , r , rt << 1 | 1));
    return res;
}
int querymi(int L,int R,int l , int r,int rt){
    int res = 1e9;
    if(L <= l && R >= r){
        return mi[rt];
    }
    int mid = (l + r) / 2;
    if(L <= mid) res = min(res , querymi(L , R , l , mid , rt << 1));
    if(R > mid) res = min(res , querymi(L , R , mid + 1 , r , rt << 1 | 1));
    return res;
}
int cal(int u , int d){
    for(int i = 1; i <= m; i ++){
        int ma = 0 , mi = 1e9;
        for(int j = u; j <= d; j ++){
            ma = max(ma , a[j][i]);
            mi = min(mi , a[j][i]);
        }
      //  printf ("%d %d\n",ma , mi);
        nowa[i] = ma;nowi[i] = mi;
    }
    build(1 , m , 1);
    int r = 1 , ans = 0;
 //   printf ("%d>>>>>>\n",queryma(1 , 1 , 1 , m ,1));
    for(int l = 1; l <= m; l ++){
        while(r <= m && queryma(l , r ,1 , m , 1) - querymi(l , r , 1 , m , 1) <= k){
            r ++;
        }
        ans = max(ans , (d - u + 1) * (r - l));
     //   printf ("%d %d????\n",l , r);
    }
    return ans;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i = 1; i <= n; i ++){
        for(int j = 1; j <= m; j ++){
            scanf("%d",&a[i][j]);
        }
    }
    scanf("%d",&k);
  //  printf ("%d\n",cal(1 , 3));
    int ans = 0;
    for(int i = 1; i <= n; i ++){
        for(int j = i; j <= n; j ++){
            ans = max(ans , cal(i , j));
        }
    }
    printf ("%d\n",ans);

    return 0;
}




  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值