链接:
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3417
题意:
输入一个n*m(1≤n,m≤1000)矩阵,每个格子可能是空地,也可能是沼泽。对于每个空地格子,
求出以它为右下角的空地矩形的最大周长,然后统计每个周长出现了多少次。
分析:
扫描法 + 单调栈。
按照从上到下的顺序处理每一行,在每一行中从左到右处理每个格子(以下称为“当前格”),
找出以该格子为右下角的最大周长矩形(以下简称最优矩形)。只要找到了以每个
格子为右下角的最优矩形,本题就可以得到解决。
假定“当前格”已经固定,则只需要再确定一个左上角,就可以得到一个矩形。
下面用c表示第c列,h表示第c列的空地高度。
首先从上到下枚举“当前行”,然后从左到右枚举“当前列”。
在移动“当前列”的过程中,保存若干个(c,h),按照c从小到大排列成有序表,
则h也是从小到大排列,并且h-c也是从小到大排列。则可以在O(1)时间
内求出每个当前格对应的最优矩形(因为最后一个矩形就是最优的),然后根据需要从右到
左删除一些矩形(也可能不删除),并且可能会把最右边的矩形变矮。然后,当且仅当新矩
形的h-c比它左边的矩形大时,加到表的最右边。由于添加和删除都在表的最右端,用一个
栈来实现即可。
代码:
1 #include <cstdio> 2 #include <cstring> 3 4 const int UP = 1000 + 5; 5 6 struct NODE { 7 int c, h; 8 } K[UP]; //栈 9 10 int hei[UP], ans[UP*2]; 11 char s[UP][UP]; 12 13 int main(){ 14 int T, n, m; 15 scanf("%d", &T); 16 while(T--){ 17 scanf("%d%d", &n, &m); 18 for(int i = 0; i < n; i++) scanf("%s", s[i]); 19 20 memset(hei, 0, sizeof(hei)); 21 memset(ans, 0, sizeof(ans)); 22 for(int r = 0; r < n; r++){ 23 int p = 0; //栈顶指针 24 for(int c = 0; c < m; c++){ 25 if(s[r][c] == '#') p = hei[c] = 0; 26 else{ 27 hei[c]++; 28 NODE nd = (NODE){c, hei[c]}; 29 if(!p) K[++p] = nd; 30 else{ 31 while(p && nd.h <= K[p].h) nd.c = K[p--].c; 32 if(!p || nd.h - nd.c > K[p].h - K[p].c) K[++p] = nd; 33 } 34 ans[K[p].h + c - K[p].c + 1]++; 35 } 36 } 37 } 38 39 for(int i = 2; i <= m + n; i++) if(ans[i]){ 40 printf("%d x %d ", ans[i], i * 2); 41 } 42 } 43 return 0; 44 }