最近遇到讨厌的最大矩形问题,很难解决
舞台(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)的时间,也不消耗空间,非常不错的好方法!
程序如下:
- <span style="font-size:18px;">var
- map:array[0..1001,0..1001]of char;
- h,le,ri,left,right:array[0..1000,0..1000]of longint; //left:点(i,j)最左边的障碍点,right:点(i,j)最右边的障碍点
- x:char;
- n,m,i,j,ans,l,r,max:longint;
- function maxx(a,b:longint):longint;
- begin
- if a>b then exit(a) else exit(b);
- end;
- function min(a,b:longint):longint;
- begin
- if a>b then exit(b) else exit(a);
- end;
- begin
- { assign(input,'drama.in');
- assign(output,'drama.out');
- reset(input);rewrite(outpuT);}
- readln(n,m);
- for i:=1 to n do
- begin
- ans:=0;
- for j:=1 to 2*m-1 do
- begin
- read(x);
- if x<>' ' then inc(ans);
- if x='R' then map[i,ans]:='.' else if x='F' then map[i,ans]:='x';
- end;
- readln;
- end;{输入}
- for i:=0 to n do begin map[i,0]:='.';map[i,m+1]:='.'; end;
- for i:=0 to m do begin map[0,i]:='.';map[n+1,i]:='.'; end;
- //在外围围成一堵墙
- for i:=1 to n do
- begin
- l:=0;
- for j:=1 to m do
- if map[i,j]='.' then
- begin
- l:=j;
- left[i,j]:=0;
- end
- else left[i,j]:=l;
- r:=m+1;
- for j:=m downto 1 do
- if map[i,j]='.' then
- begin
- r:=j;
- right[i,j]:=0;
- end
- else right[i,j]:=r;
- end;
- //求最右边的障碍点
- max:=0;
- for i:=1 to n do
- for j:=1 to m do
- if (map[i-1,j]='.')and(map[i,j]<>'.') then
- begin
- h[i,j]:=1;
- le[i,j]:=left[i,j];
- ri[i,j]:=right[i,j];
- //上述问号中应该填入的答案
- if (ri[i,j]-le[i,j]-1)*h[i,j]>max then max:=(ri[i,j]-le[i,j]-1)*h[i,j];
- end
- else if (map[i-1,j]<>'.')and(map[i,j]<>'.') then
- begin
- h[i,j]:=h[i-1,j]+1;
- le[i,j]:=maxx(le[i-1,j],left[i,j]);
- ri[i,j]:=min(ri[i-1,j],right[i,j]);
- if (ri[i,j]-le[i,j]-1)*h[i,j]>max then max:=(ri[i,j]-le[i,j]-1)*h[i,j];
- end;
- //核心思想
- writeln(max);
- //close(input);close(output);
- end.</span>
我用的是pascal