题目链接
MINSUB - Largest Submatrix
You are given an matrix M (consisting of nonnegative integers) and an integer K. For any submatrix of M' of M define min(M') to be the minimum value of all the entries of M'. Now your task is simple: find the maximum value of min(M') where M' is a submatrix of M of area at least K (where the area of a submatrix is equal to the number of rows times the number of columns it has).
Input
The first line contains a single integer T (T ≤ 10) denoting the number of test cases, T test cases follow. Each test case starts with a line containing three integers, R (R ≤ 1000), C (C ≤ 1000) and K (K ≤ R * C) which represent the number of rows, columns of the matrix and the parameter K. Then follow R lines each containing C nonnegative integers, representing the elements of the matrix M. Each element of M is ≤ 10^9
Output
For each test case output two integers: the maximum value of min(M'), where M' is a submatrix of M of area at least K, and the maximum area of a submatrix which attains the maximum value of min(M'). Output a single space between the two integers.
Example
Input: 2 2 2 2 1 1 1 1 3 3 2 1 2 3 4 5 6 7 8 9 Output: 1 4 8 2
题意:
给定一个由非负数组成的矩阵M,和一个整数K,对于矩阵M的子矩阵M’,定义min(M’)为M'矩阵中元素的最小值。
我们需要找出这样一个子矩阵,该矩阵的面积至少为K,且min(M’)最大化。面积的定义为该矩阵的行数*列数。求出min(M'),并给出使得min(M')为该值时面积的最大值。
题解:
这类问题都是可以二分答案的。把小于二分值的位置设为0,其他设为1,那么问题就变成了求全为1的子矩阵的最大面积,这件事情可以用单调栈搞(方法类似于http://www.cnblogs.com/ziyi--caolu/archive/2013/06/23/3151556.html ,事先统计出每个位置向左有多少个1)。可以预处理出每个点(i,j)的左边有多少个连续的1(记为f[i][j]),然后枚举每一列,那么这一列就可以类似与单调栈一样处理算出能上下延伸多少,做法和一位数组的单调栈一样。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
#include<stack>
using namespace std;
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define pb push_back
#define fi first
#define se second
typedef vector<int> VI;
typedef long long ll;
typedef pair<int,int> PII;
const int inf=0x3fffffff;
const ll mod=1000000007;
const int maxn=1000+100;
int r,c,k;
int a[maxn][maxn];
struct node
{
int pre,next,v;
node(int a):v(a),pre(1),next(1) {}
};
stack<node> s;
int f[maxn][maxn];
int cal(int x)
{
int ans=0;
while(!s.empty()) s.pop();
memset(f,0,sizeof(f));
rep(i,1,r+1) rep(j,1,c+1)
{
if(a[i][j]>=x) f[i][j]=f[i][j-1]+1;
else f[i][j]=0;
}
rep(j,1,c+1)
{
s.push(node(f[1][j]));
rep(i,2,r+1)
{
node t=node(f[i][j]);
while(!s.empty()&&s.top().v>=t.v) //此处>或>=都可以
{
node tt=s.top();
s.pop();
t.pre+=tt.pre;
if(!s.empty()) s.top().next+=tt.next;
ans=max(ans,tt.v*(tt.next+tt.pre-1));
}
s.push(t);
}
while(!s.empty())
{
node tt=s.top();
s.pop();
if(!s.empty()) s.top().next+=tt.next;
ans=max(ans,tt.v*(tt.next+tt.pre-1));
}
}
return ans;
}
int main()
{
int cas;
scanf("%d",&cas);
while(cas--)
{
scanf("%d%d%d",&r,&c,&k);
int mi=inf,mx=-inf;
rep(i,1,r+1) rep(j,1,c+1) scanf("%d",&a[i][j]),mi=min(mi,a[i][j]),mx=max(mx,a[i][j]);
int l=mi,r=mx,ans=0;
while(l<=r)
{
int mid=(l+r)/2;
if(cal(mid)>=k) l=mid+1,ans=mid;
else r=mid-1;
}
printf("%d %d\n",ans,cal(ans));
}
return 0;
}
后来,在深刻理解单调栈的原理之后,我又学会了单调栈的另一种写法,又短又快~
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
#include<stack>
using namespace std;
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define pb push_back
#define fi first
#define se second
typedef vector<int> VI;
typedef long long ll;
typedef pair<int,int> PII;
const int inf=0x3fffffff;
const ll mod=1000000007;
const int maxn=1000+10;
int a[maxn][maxn];
int s[maxn]; //此处栈中存的是数组中的下标
int f[maxn][maxn];
int n,m,k;
int check(int x)
{
int ans=0;
rep(i,1,n+1) rep(j,1,m+1) f[i][j]=a[i][j]>=x? f[i-1][j]+1:0; //此处与题解不同,这里是向上扩展
rep(i,1,n+1)
{
int top=0;
f[i][m+1]=0;
rep(j,1,m+2)
{
while(top&&f[i][j]<f[i][s[top]])
{
ans=max(ans,f[i][s[top]]*(j-1-s[top-1]));
top--;
}
s[++top]=j;
}
}
return ans;
}
int main()
{
int cas;
scanf("%d",&cas);
while(cas--)
{
scanf("%d%d%d",&n,&m,&k);
int mx=0,mi=2e9;
rep(i,1,n+1) rep(j,1,m+1) scanf("%d",&a[i][j]),mx=max(mx,a[i][j]),mi=min(mi,a[i][j]);
int l=mi,r=mx;
int ans=0;
while(l<=r)
{
int m=(l+r)/2;
if(check(m)>=k)
{
ans=m;
l=m+1;
}
else r=m-1;
}
printf("%d %d\n",ans,check(ans));
}
return 0;
}