[caioj]单调队列3 单调队列

【题意】 
给一个N*M的数矩阵 
现在求一个子矩阵 要求子矩阵中最大值与最小值的差<=C。而且子矩阵的宽度(横)不超过100(长(竖)没有限制)。 求子矩阵的最大面积。 
【输入格式】 
第一行两个整数 M(左右方向),N(上下方向)和 C (N,M<=500 0<=C<= 10 ) 
接下来 N行 每行M个数 每个数(-30000~30000) 
【输出格式】 
子矩阵的最大面积 
【样例输入】 
10 15 4 
41 40 41 38 39 39 40 42 40 40 
39 40 43 40 36 37 35 39 42 42 
44 41 39 40 38 40 41 38 35 37 
38 38 33 39 36 37 32 36 38 40 
39 40 39 39 39 40 40 41 43 41 
39 40 41 38 39 38 39 39 39 42 
36 39 39 39 39 40 39 41 40 41 
31 37 36 41 41 40 39 41 40 40 
40 40 40 42 41 40 39 39 39 39 
42 40 44 40 38 40 39 39 37 41 
41 41 40 39 39 40 41 40 39 40 
47 45 49 43 43 41 41 40 39 42 
42 41 41 39 40 39 42 40 42 42 
41 44 49 43 46 41 42 41 42 42 
45 40 42 42 46 42 44 40 42 41 
【样例输出】 
35

题解:

我的方法复杂度似乎是对的,但被卡了1个点?我太菜了……我的方法是先预处理出 mx[i][j][k] mn[i][j][k] ,分别表示第 i 行第 j 列的数数起 k 个数中的最大最小值,然后枚举列的起点 j 与跨度 k ,然后用一个单调队列维护最多能延伸多少行,时间复杂度为 O(100nm) 然而我还是被卡。

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=505;
int m,n,c,a[maxn][maxn],start[maxn];
int mx[maxn][maxn][105];
int mn[maxn][maxn][105];
bool mark[maxn];
struct Queue{int mx,mn;}q[maxn];
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}
int main()
{
//  freopen("airport9.in","r",stdin);
    m=read();n=read();c=read();
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    a[i][j]=read(),mx[i][j][1]=mn[i][j][1]=a[i][j];
    for(int k=2;k<=min(m,35);k++)
    for(int i=1;i<=n;i++)
    for(int j=1;j+k-1<=m;j++)
    mx[i][j][k]=max(mx[i][j][k-1],a[i][j+k-1]),
    mn[i][j][k]=min(mn[i][j][k-1],a[i][j+k-1]);
    int ans=1;
    q[0].mx=-999999999;
    q[0].mn=999999999;
    for(int i=1;i<=m;i++)start[i]=1;
    for(int k=1;k<=min(m,35);k++)
    {
        for(int j=1;j+k-1<=m;j++)
        {
            if(mark[j])continue;
            int head=1,tail=1,i;
            for(i=start[j];i<=n;i++)
            if(mx[i][j][k]-mn[i][j][k]<=c)break;
            if(i==n+1){mark[j]=true;continue;}
            start[j]=i;
            if(mx[i][j][k]-mn[i][j][k]<=c)
            {
                q[1].mx=mx[i][j][k];q[1].mn=mn[i][j][k];
                ans=max(ans,k);
                for(int l=i+1;l<=n;l++)
                {
                    if(mx[l][j][k]-mn[l][j][k]>c)
                    {
                        if((n-l)*k<=ans)break;
                        head=1;tail=0;continue;
                    }
                    while(head<=tail&&max(mx[l][j][k],q[head].mx)-min(mn[l][j][k],q[head].mn)>c)
                    {
                        head++;
                        q[head].mx=max(q[head].mx,q[head-1].mx);
                        q[head].mn=min(q[head].mn,q[head-1].mn);
                    }
                    if(head<=tail)ans=max(ans,k*(tail-head+2));
                    tail++;
                    q[tail].mx=mx[l][j][k];
                    q[tail].mn=mn[l][j][k];
                    q[head].mx=max(q[head].mx,q[tail].mx);
                    q[head].mn=min(q[head].mn,q[tail].mn);
                }
            }
        }
    }
    printf("%d\n",ans);
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值