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;
}