真题
第十四届省赛 编程题 第5题
工人砌了一面奇特的砖墙,该墙由N列砖组成(1≤N≤1e6),且每列砖的数量为Ki(1≤Ki≤1e4,相邻砖块之间无缝隙),每块砖的长宽高都为1。小蓝为了美化这面墙,需要在这面墙中找到一块面积最大的矩形用于涂鸦,那么请你帮助小蓝找出最大矩形,并输出其面积。
例如:N=6,表示这面墙有6列,每列砖的数量依次为3、2、1、5、6、2,如下图:
图中虚线部分是一块面积最大的矩形,其面积为10.
输入描述:
第一行输入一个正整数N(1≤N≤1e6),表示这面砖墙由N列砖组成
第二行输入N个正整数Ki(1≤Ki≤1e4),依次表示每列砖的数量,正整数之间以一个空格隔开输出描述:
输出一个正整数,表示最大矩形的面积样例输入:
6
3 2 1 5 6 2样例输出:
10
解题思路
1.递归(标准解法)
找到最矮的列,分别计算左中右三个面积(分治),取其最大。
中:最矮列的列高*本区域的宽度(贪心假设)
左:最矮列左侧区域的最大矩形(递归调用)
右:最矮列右侧区域的最大矩形(递归调用)
2.伸展(娃儿创新)
遍历每一列,分别计算此列为中心,向两侧延展,直至遇到较矮的列,所形成的矩形的面积。
各面积存入列表,取其最小值。
代码
思路一 (递归版)
def tj1(wall):
i=wall.index(min(wall))
sL=tj1(wall[:i]) if i>0 else 0
sR=tj1(wall[i+1:]) if i<len(wall)-1 else 0
return max(wall[i]*len(wall),sL,sR)
思路二(伸展版)
def tj2(wall):
sL=[]
for i in range(len(wall)):
s=1; h=wall[i]
for j in range(i,0,-1):
if(h>wall[j-1]):
s+=i-j; break
else:
s+=i-1
for j in range(i,len(wall)-1):
if(h>wall[j+1]):
s+=j-i; break
else:
s+=len(wall)-1-i
sL.append(s*h)
return max(sL)
(增加一个用while代替for...else的版本)
def tj2w(wall): # while版
sMax=0; N=len(wall)
for i in range(N):
w=1; h=wall[i]
j=i
while(j>0 and h<=wall[j-1]): j-=1
w+=i-j
j=i
while(j<N-1 and h<=wall[j+1]): j+=1
w+=j-i
sNew=w*h
if(sNew>sMax): sMax=sNew
return sMax
测试代码
def timeit(num=100):
t1=[];t2=[];size=[]
for i in range(num):
K=random.choices(range(1,1000001),k=random.randint(1,10000)); N=len(K)
size.append(N)
t0=time.time()
ans=tj2(K)
t2.append(time.time()-t0)
t0=time.time()
ans=tj1(K)
t1.append(time.time()-t0)
return t1,t2,size
结论
两种思路都很容易实现和调试成功,从用时看标准版整体稍多于娃儿的思路的1/2,仅2%的用例娃儿胜出(不排除python的time模块计时不准)。
但从孩子的学习成果来看,我还是感到非常欣慰,特别是python to C++时,很显然伸展版的思路更契合(不用做列表的切片)