讨厌的最大矩形

来源:互联网 发布:网络面试题 编辑:程序博客网 时间:2024/05/02 01:26

最近遇到讨厌的最大矩形问题,很难解决难过

舞台(drama)
【问题描述】
省运会开幕式表演的舞台需要足够的空间, 这样才可以挤得下尽可能多的观
众。 现选择了一个可以看做一个N*M 个格子的矩形的地方, 每个格子中标着“R”
或“F”,分别表示该格有建筑物或该格为平地可以使用。现希望舞台能够表演
在一块矩形的只包含平地的区域上,并且希望其面积最大。
请你帮助找出面积最大的那块只含平地的矩形,输出其面积。
【输入格式】
输入文件第一行包含了两个整数分别表示N,M。
接下来N 行,每行包含M 个用空格隔开的字符“F”或“R”,描述了地形。
【输出格式】
输出文件包含且仅包含一行,一个整数表示最大的矩形面积。
【输入样例】

5 6
R F F F F F
F F F F F F
R R R F F F
F F F F F F
F F F F F F

【输出样例】
15
【数据规模】
对于50%的数据,1<=N,M<=200
对于100%的数据,1<=N,M<=1000

对于此题,最直接的方法是用O((n*m)^3),可是,不超时就怪了!!

用2014年普及组初赛试题最后一题的方法,可以,但只能拿60分,O(1000^3)定超时

那用什么方法?首先得了解悬线:

有效竖线:除了两个端点外,不覆盖任何障碍点的竖直线段。
悬线:上端点覆盖了一个障碍点或达到整个矩形上端的有效竖线。
图中所示的线段均为悬线


如果把一个悬线向左右两个方向尽可能移动,就能得到一个矩形,不妨称为这个悬线对应的矩形。
悬线对应的矩形不一定是极大子矩形,因为下边界可能还可以向下扩展:

因为所有悬线对应矩形的集合一定包含了极大子矩形的集合,所以通过通过枚举所有的悬线,找出所有的极大子矩形。

算法:

H[i,j]为点(i,j)对应的悬线的长度。

L[i,j]为点(i,j)对应的悬线向左最多能够移动到的位置

R[i,j]为点(i,j)对应的悬线向右最多能够移动到的位置。

在点(i,j)不是障碍点时:

1.如果(i-1,j)为障碍点,

   那么(i,j)对应的悬线长度1,左右能移动到的位置是整个矩形的左右边界。
   即 H[i,j]=1,L[i,j]=?,R[i,j]=?(看程序)

2.如果(i-1,j)不是障碍点, 

   H[i,j]=H[i-1,j]+1,L[i,j]=max(L[i-1,j], (i-1,j)左边第一个障碍点的位置),

   R[i,j]=min(R[i-1,j], (i-1,j)右边第一个障碍点的位置

   ans=max((R[i,j]-L[i,j]-1)*H[i,j])   (1<=i<=n,1<=j<=m) 

   只需O(m*n)的时间,也不消耗空间,非常不错的好方法!

程序如下:

[plain] view plaincopy
  1. <span style="font-size:18px;">var  
  2.   map:array[0..1001,0..1001]of char;  
  3.   h,le,ri,left,right:array[0..1000,0..1000]of longint; //left:点(i,j)最左边的障碍点,right:点(i,j)最右边的障碍点  
  4.   x:char;  
  5.   n,m,i,j,ans,l,r,max:longint;  
  6. function maxx(a,b:longint):longint;  
  7. begin  
  8.   if a>b then exit(a) else exit(b);  
  9. end;  
  10. function min(a,b:longint):longint;  
  11. begin  
  12.   if a>b then exit(b) else exit(a);  
  13. end;  
  14. begin  
  15.  { assign(input,'drama.in');  
  16.   assign(output,'drama.out');  
  17.   reset(input);rewrite(outpuT);}  
  18.   readln(n,m);  
  19.   for i:=1 to n do  
  20.     begin  
  21.       ans:=0;  
  22.       for j:=1 to 2*m-1 do  
  23.         begin  
  24.           read(x);  
  25.           if x<>' ' then inc(ans);  
  26.           if x='R' then map[i,ans]:='.' else if x='F' then map[i,ans]:='x';  
  27.         end;  
  28.       readln;  
  29.     end;{输入}  
  30.   for i:=0 to n do begin map[i,0]:='.';map[i,m+1]:='.'; end;  
  31.   for i:=0 to m do begin map[0,i]:='.';map[n+1,i]:='.'; end;  
  32.   //在外围围成一堵墙  
  33.   for i:=1 to n do  
  34.     begin  
  35.       l:=0;  
  36.       for j:=1 to m do  
  37.         if map[i,j]='.' then  
  38.           begin  
  39.             l:=j;  
  40.             left[i,j]:=0;  
  41.           end  
  42.         else left[i,j]:=l;  
  43.       r:=m+1;  
  44.       for j:=m downto 1 do  
  45.         if map[i,j]='.' then  
  46.           begin  
  47.             r:=j;  
  48.             right[i,j]:=0;  
  49.           end  
  50.         else right[i,j]:=r;  
  51.     end;  
  52.   //求最右边的障碍点  
  53.   max:=0;  
  54.   for i:=1 to n do  
  55.     for j:=1 to m do  
  56.       if (map[i-1,j]='.')and(map[i,j]<>'.') then  
  57.         begin  
  58.           h[i,j]:=1;  
  59.           le[i,j]:=left[i,j];  
  60.           ri[i,j]:=right[i,j];  
  61.           //上述问号中应该填入的答案  
  62.           if (ri[i,j]-le[i,j]-1)*h[i,j]>max then max:=(ri[i,j]-le[i,j]-1)*h[i,j];  
  63.         end  
  64.       else if (map[i-1,j]<>'.')and(map[i,j]<>'.') then  
  65.         begin  
  66.           h[i,j]:=h[i-1,j]+1;  
  67.           le[i,j]:=maxx(le[i-1,j],left[i,j]);  
  68.           ri[i,j]:=min(ri[i-1,j],right[i,j]);  
  69.           if (ri[i,j]-le[i,j]-1)*h[i,j]>max then max:=(ri[i,j]-le[i,j]-1)*h[i,j];  
  70.         end;  
  71.   //核心思想  
  72.   writeln(max);  
  73.   //close(input);close(output);  
  74. end.</span>  

我用的是pascal
0 0