紫书 例题8-19 UVa 12265 (扫描法+单调栈)

首先可以用扫描法处理出一个height数组, 来保存从当前行开始, 每一个格子可以向上延伸的最大长度。

这种“延伸”的问题用扫描法, 因为往往这个时候可以利用前一次的结果来更新当前的值


然后这道题的关键就是是维护一个单调栈, 栈顶的元素就是当前状态所求的答案。

这个单调栈满足的性质是:c从小到大增加, h从小到大增加, h-c从小到大增加。c表示当前列, h表示height[c]

因为遍历的时候是从左到右的, 所以c就是一直增大的, 然后加入的时候有个while循环, 保证h是一直增大的,

最后加入的时候的if就是维护h-c是一直增大的。


这道题和防线那道题目很像, 都是维护一个双重有序的结构, 在加入新的元素的时候需要修改结构的值。只不过那道题是二分,这道题是单调栈。


#include<cstdio>
#include<cstring>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
using namespace std;

const int MAXN = 1123;
char s[MAXN][MAXN];
int height[MAXN], ans[MAXN<<1], n, m, top;

struct node
{
	int c, h;
	int val() { return h - c; }
	node(int c = 0, int h = 0) : c(c), h(h) {}
}stack[MAXN];

int main()
{
	int T;
	scanf("%d", &T);
	
	while(T--)
	{
		scanf("%d%d", &n, &m);
		REP(i, 0, n) scanf("%s", s[i]);
		
		memset(height, 0, sizeof(height));
		memset(ans, 0, sizeof(ans));
		
		REP(i, 0, n)
		{
			top = -1;
			REP(j, 0, m)
			{
				if(s[i][j] == '#')
				{
					top = -1;
					height[j] = 0;
					continue;
				}
				
				height[j]++;
				node r(j, height[j]);
				if(top < 0)  stack[++top] = r;     
				else
				{
					while(top >= 0 && r.h <= stack[top].h) r.c = stack[top--].c;
					if(top < 0 || r.val() > stack[top].val()) stack[++top] = r;
				}

				ans[j+stack[top].val()+1]++;
			}
		}
		
		REP(i, 1, n + m + 1)
			if(ans[i])
				printf("%d x %d
", ans[i], i * 2);
	}
	
	return 0;
}

原文地址:https://www.cnblogs.com/sugewud/p/9819582.html