SPOJ_MINSUB:Largest_Submatrix(二分+单调栈)

12 篇文章 0 订阅
3 篇文章 0 订阅
题目大意是说给定一个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

然后求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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值