[BZOJ 1047] [HAOI2007] 理想的正方形 【单调队列】

题目链接:BZOJ - 1047

题目分析

使用单调队列在 O(n^2) 的时间内求出每个 n * n 正方形的最大值,最小值。然后就可以直接统计答案了。

横向有 a 个单调队列(代码中是 Q[1] 到 Q[a] ),维护每行当前枚举区间的单调队列。

纵向一个单调队列(代码中是 Q[0] ),求出当前枚举区间的每行的单调队列后,就得到了每行的这个区间的最小值(最大值),就相当于一个长度为行数的数组,然后纵向做单调队列,求出的就是正方形的最值了。

代码

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>

using namespace std;

const int MaxN = 1000 + 5, INF = 999999999;

int a, b, n, Ans;
int Map[MaxN][MaxN], Q[MaxN][MaxN], F[MaxN], Head[MaxN], Tail[MaxN], Min[MaxN][MaxN], Max[MaxN][MaxN];
//Q[0]是纵向的单调队列 

inline int gmin(int a, int b) {return a < b ? a : b;}
inline int gmax(int a, int b) {return a > b ? a : b;}

void Get_Min() 
{
	for (int i = 1; i <= a; ++i) 
	{
		Head[i] = 1;
		Tail[i] = 0;
	}
	for (int i = 1; i <= b; ++i) 
	{
		for (int j = 1; j <= a; ++j) 
		{
			if (i > n && Head[j] <= Tail[j] && Q[j][Head[j]] == i - n) ++Head[j];
			while (Head[j] <= Tail[j] && Map[j][i] < Map[j][Q[j][Tail[j]]]) --Tail[j];
			Q[j][++Tail[j]] = i; 
		}	
		if (i >= n) 
		{
			Head[0] = 1; Tail[0] = 0;
			for (int j = 1; j <= a; ++j)
			{
				F[j] = Map[j][Q[j][Head[j]]];
				if (j > n && Head[0] <= Tail[0] && Q[0][Head[0]] == j - n) ++Head[0];
				while (Head[0] <= Tail[0] && F[j] < F[Q[0][Tail[0]]]) --Tail[0];
				Q[0][++Tail[0]] = j;
				if (j >= n) Min[j][i] = F[Q[0][Head[0]]]; 
			}
		}
	}
}

void Get_Max() 
{
	for (int i = 1; i <= a; ++i) 
	{
		Head[i] = 1;
		Tail[i] = 0;
	}
	for (int i = 1; i <= b; ++i) 
	{
		for (int j = 1; j <= a; ++j) 
		{
			if (i > n && Head[j] <= Tail[j] && Q[j][Head[j]] == i - n) ++Head[j];
			while (Head[j] <= Tail[j] && Map[j][i] > Map[j][Q[j][Tail[j]]]) --Tail[j];
			Q[j][++Tail[j]] = i; 
		}
		if (i >= n) 
		{
			Head[0] = 1; Tail[0] = 0;
			for (int j = 1; j <= a; ++j)
			{
				F[j] = Map[j][Q[j][Head[j]]];
				if (j > n && Head[0] <= Tail[0] && Q[0][Head[0]] == j - n) ++Head[0];
				while (Head[0] <= Tail[0] && F[j] > F[Q[0][Tail[0]]]) --Tail[0];
				Q[0][++Tail[0]] = j;
				if (j >= n) Max[j][i] = F[Q[0][Head[0]]]; 
			}
		}
	}
}

int main()
{
	scanf("%d%d%d", &a, &b, &n);
	for (int i = 1; i <= a; ++i) 
		for (int j = 1; j <= b; ++j) 
			scanf("%d", &Map[i][j]);
	Get_Min();
	Get_Max();
	Ans = INF;
	for (int i = n; i <= a; ++i) 
		for (int j = n; j <= b; ++j) 
			Ans = gmin(Ans, Max[i][j] - Min[i][j]);
	printf("%d
", Ans);
	return 0;
}

  

原文地址:https://www.cnblogs.com/JoeFan/p/4319732.html