我正在练习算法,而且我已经坚持了几天这个问题。当我测试我的解决方案时,我仍然是错误的。这是问题陈述:
纽约华尔街以其令人叹为观止的摩天大楼而闻名。 但是下雨的季节即将来临,而且水的数量也在增加 将落在建筑物上将是巨大的今年。以来 每栋建筑都被固定在左侧和右侧的建筑物上 (除了第一个和最后一个),水可以从a泄漏 只有当建筑物的高度高于高度时才建造 建筑物的左侧或右侧(边缘的高度) 华尔街是0)。所有建筑物的宽度均为1米。您 给出了华尔街建筑物的高度(米) 从左到右,您的任务是打印到标准输出 (stdout)持有的总水量(立方米) 华尔街的建筑物。
示例输入:
heights: [9 8 7 8 9 5 6]
示例输出:
5
说明: 在这个例子中,在第一个和第五个建筑物之间有4立方米的水(1个超过第二个,2个超过第三个,1个超过第四个),而在第五个和第七个建筑物之间有1立方米持有水(在第六座建筑物上)。
我解决这个问题的方法是找到全局最大值,并使用这些最大值的差异来计算积水量。我考虑到最后使用local_water变量可能遗漏的水。任何人都可以帮我找到算法或代码中的错误吗?
注意:我正在寻找一种只能通过每个元素的解决方案
以下是我输入错误的输入:
heights: [8,8,4,5]
此输入应该产生1
,而不是我的回答0
。
这是我的代码:
def skyscrapers(heights):
heights.insert(0,0)
heights.append(0)
local_max = 0
global_max = 0
total_water = 0
local_water = 0
end_water = []
# end_water records water heights to be used for finding
# water between the final global maximum and
# subsequent local maximums. These potential values are
# stored in local_water.
for i in range(1, len(heights)-1):
end_water.append(heights[i])
# check for local max
if heights[i-1] < heights[i] and heights[i] > heights[i+1]:
# record potential collected water for after final
# gloabl max
for s in end_water:
local_water += (heights[i] - s) * (heights[i] - s > 0)
local_max=i
# new global max
if heights[local_max] > heights[global_max]:
for s in heights[global_max:local_max]:
if heights[global_max] - s > 0:
total_water += heights[global_max] - s
global_max = local_max
local_water = 0
end_water = []
total_water += local_water
print total_water
答案 0 :(得分:6)
height
_ _
9 | |_ _| | _ _
8 | |_| | | |
7 | | _ | |
6 | |_| | | | _
5 | | | |_| |
4 | | | | _ _
3 | | | | | | _ | |
2 | | | | | |_| |_| |
1 |0 1 2 3 4 5 6| |0 1 2 3| |0 1 2 3 4| pos
这是基于stack-based solution Maximize the rectangular area under Histogram问题的单通(!)(O(n) - 时间)O(n)空间算法:
from collections import namedtuple
Wall = namedtuple('Wall', 'pos height')
def max_water_heldover(heights):
"""Find the maximum amount of water held over skyscrapers with *heights*."""
stack = []
water_held = 0 # the total amount of water held over skyscrapers
for pos, height in enumerate(heights):
while True:
if not stack or height < stack[-1].height: # downhill
stack.append(Wall(pos + 1, height)) # push possible left well wall
break
else: # height >= stack[-1].height -- found the right well wall/bottom
bottom = stack.pop().height
if stack: # if there is the left well wall
well_height = min(stack[-1].height, height) - bottom
if well_height:
water_held += (pos - stack[-1].pos) * well_height
return water_held
示例:
>>> max_water_heldover([9, 8, 7, 8, 9, 5, 6])
5
>>> max_water_heldover([8, 8, 4, 5])
1
>>> max_water_heldover([3, 1, 2, 1, 3])
5
我已经用蛮力O(n * m)算法对其进行了测试:
from itertools import product
def test(max_buildings=6, max_floors=6):
for nbuildings, nfloors in product(range(max_buildings), range(max_floors)):
for heights in product(range(nfloors), repeat=nbuildings):
assert max_water_heldover(heights) == max_water_heldover_bruteforce(heights), heights
其中max_water_heldover_bruteforce()
是:
import sys
from colorama import Back, Fore, Style, init # $ pip install colorama
init(strip=not sys.stderr.isatty())
def max_water_heldover_bruteforce(heights):
if not heights: return 0
BUILDING, AIR, WATER = ['B', ' ',
Back.BLUE + Fore.WHITE + 'W' + Fore.RESET + Back.RESET + Style.RESET_ALL]
matrix = [[WATER] * len(heights) for _ in range(max(heights))]
for current_floor, skyscrapers in enumerate(matrix, start=1):
outside = True
for building_number, building_height in enumerate(heights):
if current_floor <= building_height:
outside = False
skyscrapers[building_number] = BUILDING
elif outside:
skyscrapers[building_number] = AIR
for i, building_height in enumerate(reversed(heights), 1):
if current_floor > building_height:
skyscrapers[-i] = AIR
else:
break
if '--draw-skyscrapers' in sys.argv:
print('\n'.join(map(''.join, matrix[::-1])), file=sys.stderr)
print('-'*60, file=sys.stderr)
return sum(1 for row in matrix for cell in row if cell == WATER)
结果是一样的。
答案 1 :(得分:6)
这是一个改进了刘志东和J.S.的一次性解决方案。 Sebastian的解决方案只使用O(1)空间:
def fillcount(elevations):
start = 0
end = len(elevations) - 1
count = 0
leftmax = 0
rightmax = 0
while start < end:
while start < end and elevations[start] <= elevations[start + 1]:
start += 1
else:
leftmax = elevations[start]
while end > start and elevations[end] <= elevations[end - 1]:
end -= 1
else:
rightmax = elevations[end]
if leftmax < rightmax:
start += 1
while start < end and elevations[start] <= leftmax:
count += leftmax - elevations[start]
start += 1
else:
end -= 1
while end > start and elevations[end] <= rightmax:
count += rightmax - elevations[end]
end -= 1
return count
我针对这个更简单的双程解决方案进行了测试:
def fillcount_twopass(elevations):
global_max = max(range(len(elevations)), key=lambda x: elevations[x])
count = 0
local_max = 0
for i in xrange(0, global_max):
if elevations[i] > local_max:
local_max = elevations[i]
else:
count += local_max - elevations[i]
local_max = 0
for i in xrange(len(elevations) - 1, global_max, -1):
if elevations[i] > local_max:
local_max = elevations[i]
else:
count += local_max - elevations[i]
return count
双程解决方案基于以下逻辑:
这与Rémi建议的类似,但使用全局最大值来提供锚点,这简化了事情。
一次性解决方案部分基于Mark Tolonen的想法。它通过观察我们可以同时执行左右传递,而不知道全局最大值来改进上述内容。这是因为任何一方的当前最大值要么大于,低于或等于另一方的最大值。即使我们还不知道全局最大值是多少,所以较低的最大值总是低于全局最大值,因此我们可以从那里继续到那一侧的下一个局部最大值。该算法详细说明:
start
和end
开始,并将left_max
,right_max
和count
初始化为0
。 start
,直到达到左边最大值。然后向左扫描,递减end
,直到达到正确的最大值。 count
。 start
和end
重合时结束。 请注意,就我们的目的而言,局部最大值只是在上升之前的任何点(可能是一个高原),然后是下降。到目前为止遇到的最高局部最大值以下的局部最大值仅在步骤3中遇到,其中它们没有效果。
最后一个解决方案可以在3秒内处理一千万个数据点:
>>> rands = [random.randrange(0, 1000000) for i in xrange(10000000)]
>>> %timeit fillcount(rands)
1 loops, best of 3: 3.3 s per loop
答案 2 :(得分:2)
class Solution:
# @param A, a list of integers
# @return an integer
def trap(self, A):
uTrapper = []
i = 0
leftIndex = 0
rightIndex = 0
# only 2 left could not trap water
while (i < (len(A) - 2)):
leftIndex = self.findLeftBank((A[i:])) + i
rightIndex = self.findRightBank((A[leftIndex+1:]), A[leftIndex]) + leftIndex + 1
uTrapper.append((A[leftIndex : rightIndex+1]))
i = rightIndex
return self.getRainWater(uTrapper)
def findLeftBank(self, list):
for i in range(len(list)):
curr = list[i]
currNext = list[i+1] if i+1 < len(list) else 0
if(curr > currNext):
return i
return len(list) - 1
def findRightBank(self, list, leftHight):
biggestLoc = len(list)
biggest = 0
for i in range(len(list)):
if(list[i] >= leftHight):
return i
if(list[i] > biggest):
biggestLoc = i
biggest = list[i]
return biggestLoc
def getRainWater(self, lists):
all = 0
for i in range(len(lists)):
list = lists[i]
height = min(list[0], list[len(list)-1])
for i in range(1, len(list)-1):
if(list[i] > height):
continue
all = all + (height - list[i])
return all
s = Solution()
print s.trap([9,6,8,8,5,6,3])
上面好吗?
答案 3 :(得分:0)
我想提出一种看起来很直观的解决方案。
战略:该策略是在建筑物的高度中找到局部最大值,并在每对最大值(如果有)的中间,以累积水量:
代码:
from scipy.signal import argrelextrema
import numpy as np
def local_max_scipy(a):
minima = argrelextrema(a, np.greater_equal)[0]
return minima
def water_cumulative(buildings):
local_maxima=local_max_scipy(buildings)
water = 0
# 2 or more maxima => there is water
if len(local_maxima)>1:
# in the middle of every couple of local maxima
for i in range((len(local_maxima)-1)):
reference = 0
#pick the shorter building between the two maxima as reference
if buildings[local_maxima[i]] >= buildings[local_maxima[i+1]]:
reference = buildings[local_maxima[i+1]]
else:
reference = buildings[local_maxima[i]]
#cumulate the water
for j in range(local_maxima[i+1]-local_maxima[i]):
# just exit when building higher than the reference is found
if buildings[local_maxima[i]+1+j] < reference:
water = water + reference - buildings[local_maxima[i]+1+j]
else:
break
return water
测试:
该功能已通过以下测试:
buildings01 = np.array([3, 2, 1, 4, 5])
buildings02 = np.array([1, 2, 3, 4, 5])
buildings03 = np.array([9, 8, 7, 8, 9, 5, 6])
buildings04 = np.array([8, 8, 4, 5])
输出为:
>>>water_cumulative(buildings01)
3
>>>water_cumulative(buildings02)
0
>>>water_cumulative(buildings03)
5
>>>water_cumulative(buildings04)
1
答案 4 :(得分:0)
我将使用R软件提供答案。我对此的态度 问题如下:创建3个功能:
Startchoice()
选择开始的矢量,例如舍弃连续的建筑物的高度
不低于自己。
SectorCalculation()
使用Startchoice()返回的向量创建向量的小列表,以计算建筑物之间的水域。如果在第一个值之后找到局部最大值或全局最大值,则计算机将迭代向量的索引值,直到找到以下局部最大值或全局最大值,并将向量的子集存储到列表中的该点为止,从而消除了该问题。向量的一部分,但最后一个值再次将其传递给公式,并重做相同的步骤,直到
向量全部完成。
然后,AreaCalculation()
将用于为输出列表中的每个向量计算它们可以处理的水。
main()
函数仅获取初始向量,调用上述函数并求和
由建筑物的每个小向量得出,并给出整数的一般结果。下面提供了一些示例:
SectorCalculation<- function(vector){
## It outputs in a list the different side roofs
counter<-1
vectorList<- list()
## While vector is larger than 2
## Choose the max value in the string appart from the left value
### If it finds it, then that is the sector, eliminate it and start again
### the procedure with the part of the vector left
### Else , go to next index
while(length(vector)>2){
vecleft<-StartChoice(vector)
if(all.equal(vecleft,rep(0,length(vecleft)))!=TRUE){
global_max<- max(vecleft[2:length(vecleft)])
left<- vecleft[1]
for(i in 2:length(vecleft)){
if(i > length(vecleft)){
return(vectorList)}
else{
if(vecleft[i]==global_max){
vectorList[[counter]]<-vecleft[1:i]
vector<- vecleft[i:length(vecleft)]
counter<- counter+1
break
}
}
}
}else{return(0)}
}
return(vectorList)
}
StartChoice<- function(vecHeights){
## It gives back the vector discarding zero values at the beginning
leftval<-integer(0)
ChooseStart <- TRUE
for(i in 1:length(vecHeights)){
if(length(leftval)==0){
leftval[1]<- vecHeights[i]
}
else if(i == length(vecHeights)){return(0)}
else {
if(vecHeights[i] >= leftval){
leftval[1]<- vecHeights[i]
}
else{
ChooseStart <- FALSE
vectorleft<-vecHeights[(i-1):length(vecHeights)]
break
}
}
}
return(vectorleft)
}
AreaCalculation<-function(HeightsPassed){
## Select the second highest value between left and right and
## compute the value in the roof
highest<- min(HeightsPassed[1], HeightsPassed[length(HeightsPassed)])
Res<-(highest-HeightsPassed)[(highest-HeightsPassed)>0]
# Selecting only those values higher than 0
TotWater<-sum(Res)
return(TotWater)
}
main<-function(Heights){
## if value containing values <= 0, out
if(all.equal((Heights <= 0), rep(FALSE, length(Heights)))!=TRUE){
stop("Either values equal or lower than 0 or non-numeric values supplied")
}
## Get in a list all the sectors to be calculated
MyHeightslist<-SectorCalculation(Heights)
## If list different than a list 0 with a 0, then
## just calculate by parts the water in the roofs; then sum it
if(all.equal(MyHeightslist[[1]],rep(0,length(MyHeightslist[[1]])))!=TRUE)
{
byParts<-sapply(MyHeightslist, AreaCalculation)
TotalWater<-sum(byParts)
}
else{return(0)}
return(TotalWater)
}
下面提供了一些示例以了解其工作原理:),
main(c(1,2,3))
[1] 0
main(c(9,8,7,8,9,5,6))
[1] 5
main(c(8,9,9,8,7,8,9,5,6))
[1] 5
main(c(8, 8, 4, 5))
[1] 1
main(c(3,1,3,1,1,3))
[1] 6
main(c(1,2,3,4))
[1] 0
main(c(3,2,1))
[1] 0
干杯!,