题目大意是说给定一个R*C的非负矩阵,试求出一个包含数字数>=K的子矩阵,使得这个子矩阵中最小的数字最大.
解题思路主要是二分答案,即二分那个最小的数字,然后针对每次二分的值mid,可以将原矩阵根据是否满足A[i][j]>=mid,而转成一个R*C的01矩阵a[][],然后问题求变成了给定一个二维01矩阵,求最大的满1矩阵,可以用单调栈的思想来处理这个子问题,设b[i][j]表示从a[i][j]的位置(包含)向上最多有几个连续的1存在
比如 1 1 0
1 0 1
的b[][]={{1,1,0},{2,0,1}}
然后对于每个i单独处理第i行(即子矩阵是以第i行为下边界的情况),设lef[i][j]表示从j开始向左满足b[i][k]>=b[i][j]条件所能延伸到的最大的位置,而rig[i][j]类似表示相应能够到的最右的位置
比如 j=1 2 3 4 5 6
b[][j]=4 1 3 2 5 6
则lef[][]=1 1 3 3 5 6
rig[][]=1 6 3 6 6 6
解题思路主要是二分答案,即二分那个最小的数字,然后针对每次二分的值mid,可以将原矩阵根据是否满足A[i][j]>=mid,而转成一个R*C的01矩阵a[][],然后问题求变成了给定一个二维01矩阵,求最大的满1矩阵,可以用单调栈的思想来处理这个子问题,设b[i][j]表示从a[i][j]的位置(包含)向上最多有几个连续的1存在
比如 1 1 0
1 0 1
的b[][]={{1,1,0},{2,0,1}}
然后对于每个i单独处理第i行(即子矩阵是以第i行为下边界的情况),设lef[i][j]表示从j开始向左满足b[i][k]>=b[i][j]条件所能延伸到的最大的位置,而rig[i][j]类似表示相应能够到的最右的位置
比如 j=1 2 3 4 5 6
b[][j]=4 1 3 2 5 6
则lef[][]=1 1 3 3 5 6
rig[][]=1 6 3 6 6 6
然后求lef[][]和rig[][]的过程对于每行可以运用单调栈在O(n)时限内完成,然后这样一次验证的时间复杂度是O(n^2),算上外层的二分过程,总体复杂度为O(N^2*logN),可以通过此题.
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
using namespace std;
const int N=2008;
#define maxx(x,y) (((x)>(y))?(x):(y))
int n,m,tar,a[N][N],b[N][N],lef[N][N],rig[N][N],d[N],ans;
int read(void)
{
char c=getchar();while(c<'0'||c>'9')c=getchar();
int t=c-48;for(c=getchar();c>='0'&&c<='9';c=getchar())t=(t<<1)+(t<<3)+c-48;
return t;
}
int pan(int x)
{
int i,j,ans;ans=0;
for(j=1;j<=m;j++)b[1][j]=(a[1][j]>=x);
for(i=2;i<=n;i++)for(j=1;j<=m;j++)b[i][j]=(a[i][j]>=x)?(1+b[i-1][j]):0;
for(i=1;i<=n;i++)
{
d[d[0]=1]=lef[i][1]=1;
for(j=2;j<=m;j++)
{
while(d[0]>0&&b[i][d[d[0]]]>=b[i][j])d[0]--;
lef[i][j]=(d[0])?(d[d[0]]+1):1;d[++d[0]]=j;
}
d[d[0]=1]=rig[i][m]=m;
for(j=m-1;j>=1;j--)
{
while(d[0]>0&&b[i][d[d[0]]]>=b[i][j])d[0]--;
rig[i][j]=(d[0])?(d[d[0]]-1):m;d[++d[0]]=j;
}
for(j=1;j<=m;j++)if(b[i][j])ans=maxx(ans,b[i][j]*(rig[i][j]-lef[i][j]+1));
}
return (ans>=tar)?ans:0;
}
int main(void)
{
int i,j,t,left,right,mid;
t=read();
while(t--)
{
ans=0;n=read();m=read();tar=read();
for(i=1;i<=n;i++)for(j=1;j<=m;j++)a[i][j]=read();
left=0;right=1000000000;
while(left+1<right)
{
mid=(left+right)>>1;
if(pan(mid))left=mid;else right=mid-1;
}
if(pan(right))left=right;
printf("%d %d\n",left,pan(left));
}
return 0;
}