Description
糖果盒 ( Candy Box )
一个被分为 n*m 个格子的糖果盒,第 i 行第 j 列位置的格子里面有 a [ i ][ j ] 颗糖。
本来 tenshi 打算送这盒糖果给某 PPMM 的,但是就在要送出糖果盒的前一天晚上,一只极其可恶的老鼠夜袭糖果盒
有部分格子被洗劫并且穿了洞。tenshi 必须尽快从这个糖果盒里面切割出一个矩形糖果盒,新的糖果盒不能有洞
并且 tenshi 希望保留在新糖果盒内的糖的总数尽量多。
Input
请帮tenshi设计一个程序 计算一下新糖果盒最多能够保留多少糖果。
Output
第一行有两个整数 n、m。第 i + 1 行的第 j 个数表示 a [ i ][ j ],如果这个数为 0
则表示这个位置的格子被洗劫过。其中:
1 ≤ n,m ≤ 1000
0 ≤ a [ i ][ j ]≤ 255
Sample Input
输出最大糖果数
Sample Output
3 4
1 2 3 4
5 0 6 3
10 3 4 0
Hint
17
(这题是原题的加强版)
题目描述
一个被分为个格子的月饼盒,第行第列位置的格子里面有个月饼。本来CCC老师打算送这盒月饼给某人的,但是就在要送出月饼盒的前一天晚上,一只极其可恶的老鼠夜袭月饼盒,有部分格子被洗劫并且穿了洞。CCC老师必须尽快从这个月饼盒里面切割出一个矩形月饼盒,新的月饼盒不能有洞,并且CCC老师希望保留在新月饼盒内的月饼的总数尽量多。任务:请帮CCC老师设计一个程序 计算一下新月饼盒最多能够保留多少月饼。 如果,则表示这个格子是洞.
算法分析
这个题目是: 给你一个矩阵,要求你找一个子矩阵,使得子矩阵内不包含,并且矩阵内元素之和最大.
首先下一个定义,有效矩形: 内部不包含0元素的矩形. 极大子矩形: 4个边界都不能向外扩展的有效矩形. 由于,因此最大子矩形一定是极大子矩形
如何能够有效利用极大子矩形的性质成为了问题的关键.
设表示能够向上扩展的最大长度,也就是说最大可以扩展到它上方的第一个洞的下方,或者一直扩展到最上面的边界. 我们称到的这一条线段为能够向上扩展的最长线段. 枚举以能够向上扩展的最长线段为中心,计算能够向左扩展的最大长度,能够向右扩展的最大长度.
注意: 这样扩展出来的矩形不一定是极大子矩形,上,左,右3个边界不可以再向外扩展了,但是可能还可以向下扩展. 但是可以证明这样扩展出来的矩形集合是一定包含了极大子矩形的集合的.也就是说,我们求出集合中的所有矩形中元素之和最大的矩形就一定是最大子矩形了.
这个算法的时间复杂度是O(mn)的,已经达到了理论下界了,至此问题已经得到很好的解决.
var ans,high,right,left,i,j,k,t,s,n,m:longint; sum,a,l,r,h:array[-10..1000,-10..1000] of longint; function min(p,q:longint):longint; begin if p>q then exit(q); exit(p); end; begin readln(n,m); for i:=1 to n do begin for j:=1 to m do begin read(a[i,j]); sum[i,j]:=sum[i,j-1]+sum[i-1,j]-sum[i-1,j-1]+a[i,j]; end; readln; end; for i:=1 to n do //L[i,j]代表(i,j)这个点向左伸展的长度为多少 begin for j:=1 to m do if a[i,j]=0 then l[i,j]:=0 else l[i,j]:=l[i,j-1]+1; for j:=m downto 1 do //R[i,j]代表(i,j)这个点向右伸展的长度为多少 if a[i,j]=0 then r[i,j]:=0 else r[i,j]:=r[i,j+1]+1; end; for i:=1 to n do //H[i,j]代表(i,j)这个点向上伸展的长度为多少 for j:=1 to m do if a[i,j]<>0 then if a[i-1,j]=0 then h[i,j]:=1 else begin h[i,j]:=h[i-1,j]+1; l[i,j]:=min(l[i-1,j],l[i,j]);//此时L代表以(i,j)为右下角的那个矩形向左伸展的长度 r[i,j]:=min(r[i-1,j],r[i,j]);////此时R代表以(i,j)为右下角的那个矩形向右伸展的长度 end; for i:=1 to n do for j:=1 to m do begin left:=j-l[i,j]+1; right:=j+r[i,j]-1; high:=i-h[i,j]+1; if ans<sum[i,right]-sum[high-1,right]-sum[i,left-1]+sum[high-1,left-1] then ans:=sum[i,right]-sum[high-1,right]-sum[i,left-1]+sum[high-1,left-1] ; end; writeln(ans); end.
我的代码
var sum,l,r,ll,rr,h,a:array[0..1000,0..1000] of longint; x,y,z,m,ans,max,i,j,k,n:longint; function min(x,y:longint):longint; begin if x<y then exit(x) else exit(y); end; begin read(n,m); for i:=1 to n do for j:=1 to m do read(a[i,j]); for i:=0 to n do for j:=0 to m do sum[i,j]:=sum[i-1,j]+sum[i,j-1]-sum[i-1,j-1]+a[i,j]; for i:=1 to n do begin for j:=1 to m do if a[i,j]<>0 then l[i,j]:=l[i,j-1]+1; for j:=m downto 1 do if a[i,j]<>0 then r[i,j]:=r[i,j+1]+1; for j:=1 to m do if a[i,j]<>0 then begin h[i,j]:=h[i-1,j]+1; if a[i-1,j]=0 then begin ll[i,j]:=l[i,j]; rr[i,j]:=r[i,j]; end else begin ll[i,j]:=min(l[i,j],ll[i-1,j]); rr[i,j]:=min(r[i,j],rr[i-1,j]); end; end; end; for i:=1 to n do for j:=1 to m do if a[i,j]<>0 then begin x:=i-h[i,j]+1; y:=j-ll[i,j]+1; z:=j+rr[i,j]-1; ans:=sum[i,z]+sum[x-1,y-1]-sum[i,y-1]-sum[x-1,z]; if ans>max then max:=ans; end; writeln(max); end.