UVa 12265 - Selling Land

链接:

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\n", ans[i], i * 2);
41         }
42     }
43     return 0;
44 }

 

posted @ 2018-01-20 18:52  Ctfes  阅读(175)  评论(0编辑  收藏  举报