SPOJ 20863 MINSUB - Largest Submatrix(二分+单调栈)

Description
给出一个n*m的矩形和一整数k,问这个矩阵的所有面积不小于k的子矩阵中最小值的最大值ans,和所有最小值等于ans的面积不小于k的子矩阵的最大面积S
Input
第一行一整数T表示用例组数,每组用例输入三个整数n,m,k分别表示矩阵规模和要求的子矩阵面积下限,之后一个n*m矩阵a[i][j] (T<=10,n,m<=1000,k<=n*m,a[i][j]<=1e9)
Output
对于每组用例输出两个整数ans和S表示所有面积不小于k的子矩阵中最小值的最大值,以及所有最小值等于ans的面积不小于k的子矩阵的最大面积S
Sample Input
2
2 2 2
1 1
1 1
3 3 2
1 2 3
4 5 6
7 8 9
Sample Output
1 4
8 2
Solution
二分答案,对于一个二分值x,将矩阵中所有小于x的位置看作0,其他位置是1,那么问题变为求这个01矩阵中全为1的子矩阵面积最大值,首先求出u[i][j]表示从(i,j)开始往上有多少连续的1,之后对于每行用两遍单调栈维护每个点作为最小值能够延续的最长的区间,比如对第i行第j个点其作为最小值的区间是[l[j],r[j]],那么以第i行为下底且有(i,j)点的矩形面积最大为u[i][j]*(r[j]-l[j]+1),更新这个面积的最大值,如果不小于k就更新答案ans,变小二分值,否则说明当前二分值太大(1太少)
Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
#define maxn 1111
int T,n,m,k,a[maxn][maxn],up[maxn][maxn],s[maxn],p,l[maxn],r[maxn];
int check(int x)
{
    for(int j=1;j<=m;j++)
    {
        up[0][j]=0;
        for(int i=1;i<=n;i++)
            if(a[i][j]>=x)up[i][j]=up[i-1][j]+1;
            else up[i][j]=0;
    }
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        p=1,s[1]=1,l[1]=1;
        for(int j=2;j<=m;j++)
        {
            while(p&&up[i][s[p]]>=up[i][j])p--;
            if(!p)l[j]=1;
            else l[j]=s[p]+1;
            s[++p]=j;
        }
        p=1,s[1]=m,r[m]=m;
        for(int j=m-1;j>=1;j--)
        {
            while(p&&up[i][s[p]]>=up[i][j])p--;
            if(!p)r[j]=m;
            else r[j]=s[p]-1;
            s[++p]=j;
        }
        for(int j=1;j<=m;j++)ans=max(ans,up[i][j]*(r[j]-l[j]+1));
    }
    if(ans<k)return -1;
    return ans;
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&n,&m,&k);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                scanf("%d",&a[i][j]);
        int l=0,r=1e9,mid,ans,S;
        while(l<=r)
        {
            mid=(l+r)>>1;
            int temp=check(mid);
            if(temp!=-1)ans=mid,S=temp,l=mid+1;
            else r=mid-1;
        }
        printf("%d %d\n",ans,S);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值