【2019牛客多校补题计划】第三场:F-Planting Trees

题目链接:

https://ac.nowcoder.com/acm/contest/883/F

题目大意:

给出一个 N ∗ N N*N NN矩阵,求其面积最大的子矩阵。子矩阵满足:最大值与最小值之差小于等于给定的数 M M M

解题思路:

此题 N < = 500 N<=500 N<=500,强行暴力的话会写出 O ( N 6 ) O(N^6) O(N6)的假算法。题目里提示算法复杂度可能是 O ( N 3 ) O(N^3) O(N3)
不难想到遍历左边界时遍历右边界,这样可以得到一个竖条状区域,然后用双指针从上到下扫一遍,为了保证查询条状区域的宽中的最值不超时,需要用ST表预处理N行,这样复杂度是 O ( N 2 l o g N + N 3 ) O(N^2logN+N^3) O(N2logN+N3)
然而貌似因为测试组数太多,常数太大,交上去发现超时了。。。
左右边界似乎是必须要遍历的,只能在条状区域的答案求解上做优化。如果舍弃掉ST表,也就是放弃查询宽的最值,又该如何在仅剩的 O ( N ) O(N) O(N)复杂度里求出答案呢?
双指针+ST表的查询已经够快了,ST表的预处理都不能接受说明不应该造出条状区域单独求解。换句话说,应该在前面的 O ( N 2 ) O(N^2) O(N2)中处理些什么。这就离正确答案很近了。
为了绕开直接求宽的最值,我们应该考虑在遍历右边界时就算出当前最好的答案。也就是要用 O ( N ) O(N) O(N)的代价算出某一列的答案,并且这一列的最值性质能影响到后续的列。使用单调队列就能做到这两点,详情见代码(实现时是枚举上下边界)。

AC代码:

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
#define INF 0x7FFFFFFF

inline int read(){
    int res = 0, w = 0; char ch = 0;
    while(!isdigit(ch)){
        w |= ch == '-', ch = getchar();
    }
    while(isdigit(ch)){
        res = (res << 3) + (res << 1) + (ch ^ 48);
        ch = getchar();
    }
    return w ? -res : res;
}

int mp[505][505],n,m;
int mx[505],mi[505],miq[505],mxq[505];

int main()
{
	int T;
	T = read();
	while(T--)
	{
		n = read();m = read();
		memset(mp,0,sizeof(mp));
		for (int i=1;i<=n;i++)
			for (int j=1;j<=n;j++)
				mp[i][j] = read();
		
		int ans=0;
		int mxh,mxt,mih,mit,l,r,len;
		for(int x1=1;x1<=n;++x1)//枚举上边界 
		{
			for(int i=1;i<=n;++i)
				mi[i]=1e5+10,mx[i]=0;	
			for(int x2=x1;x2<=n;++x2)//枚举下边界 
			{
				l=1;mih=1;mit=0;mxh=1;mxt=0;
				for(int i=1;i<=n;++i)//枚举右边界 
				{
					//注意,这里放心地只对mp[x2][]这一行做单调队列,是因为先枚举上边界后枚举下边界,每行x2都会被看一次,上一行的结果等到循环到下一行时并不会清空,所以[1...i]的最值其实是考虑了x1到x2的所有行的[1...i]列的最值 
					mi[i]=min(mi[i],mp[x2][i]);
					mx[i]=max(mx[i],mp[x2][i]);
					while(mih<=mit && mi[i]<=mi[miq[mit]])
						--mit;//mi[i]入队时弹出所有比它大的 
					miq[++mit]=i;
					while(mxh<=mxt && mx[i]>=mx[mxq[mxt]])
						--mxt;
					mxq[++mxt]=i;
					while(mih<=mit && mxh<=mxt && mx[mxq[mxh]]-mi[miq[mih]]>m)
					{
						l=min(mxq[mxh]+1,miq[mih]+1);
						while(mih<=mit && miq[mih]

你可能感兴趣的:(2019牛客多校补题计划)