Python 给定建筑物高度的积水求解算法

Python 给定建筑物高度的积水求解算法,python,algorithm,Python,Algorithm,我正在练习算法,我在这个问题上已经坚持了几天了。当我测试我的解决方案时,我仍然不正确。以下是问题陈述: 纽约的华尔街以其惊人的摩天大楼而闻名。 但是雨季即将来临,而且 今年将有巨大的建筑物倒塌。自从 每一栋建筑都被贴在其左右两侧的建筑上 (除了第一个和最后一个),水可能从管道中泄漏 仅当建筑物的高度高于 建筑的左侧或右侧(边缘的高度 华尔街指数为0)。所有的建筑物都有1米宽。你 给出了华尔街建筑物的高度(以米为单位) 从左到右,您的任务是打印到标准输出 (stdout)在水面上的总水量(以立方米

我正在练习算法,我在这个问题上已经坚持了几天了。当我测试我的解决方案时,我仍然不正确。以下是问题陈述:

纽约的华尔街以其惊人的摩天大楼而闻名。 但是雨季即将来临,而且 今年将有巨大的建筑物倒塌。自从 每一栋建筑都被贴在其左右两侧的建筑上 (除了第一个和最后一个),水可能从管道中泄漏 仅当建筑物的高度高于 建筑的左侧或右侧(边缘的高度 华尔街指数为0)。所有的建筑物都有1米宽。你 给出了华尔街建筑物的高度(以米为单位) 从左到右,您的任务是打印到标准输出 (stdout)在水面上的总水量(以立方米为单位) 华尔街的建筑物

输入示例:

heights: [9 8 7 8 9 5 6]
5
示例输出:

heights: [9 8 7 8 9 5 6]
5
说明: 在本例中,第一栋和第五栋建筑之间有4立方米的水(第二栋建筑有1立方米,第三栋建筑有2立方米,第四栋建筑有1立方米),第五栋和第七栋建筑之间有1立方米的水(第六栋建筑有1立方米)

我解决这个问题的方法是找到全局最大值,并利用这些最大值的差异来计算水的积累。我用局部水变量解释了最后可能错过的水。有人能帮我找到算法或代码中的错误吗


注意:我正在寻找一种只通过每个元素一次的解决方案

以下是我有错误的输入:

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
def摩天大楼(高度):
高度。插入(0,0)
高度。追加(0)
局部_max=0
全局_max=0
总水=0
本地水=0
结束水=[]
#end_water记录用于查找的水位高度
#最终全球最大值和最大值之间的水
#随后的局部最大值。这些潜在价值是
#储存在当地的水里。
对于范围(1,长度(高度)-1)内的i:
结束水。附加(高度[i])
#检查本地最大值
如果高度[i-1]<高度[i]和高度[i]>高度[i+1]:
#记录最终试验后收集的潜在水
#gloabl最大值
对于末端水中的s:
局部水+=(高度[i]-s)*(高度[i]-s>0)
局部_max=i
#新全局最大值
如果高度[本地最大值]>高度[全局最大值]:
对于s高度[全局最大值:局部最大值]:
如果高度[global_max]-s>0:
总水+=高度[全局最大值]-s
全局最大值=局部最大值
本地水=0
结束水=[]
总水+=当地水
打印总水量
类解决方案:
#@param A,整数列表
#@返回一个整数
def存水弯(自身,A):
uTrapper=[]
i=0
leftIndex=0
rightIndex=0
#只剩下两个人没能存水
而(i)<(len(A)-2)):
leftIndex=self.findLeftBank((A[i:])+i
rightIndex=self.findRightBank((A[leftIndex+1:]),A[leftIndex])+leftIndex+1
追加((A[leftIndex:rightIndex+1]))
i=右索引
返回自我获取雨水(uTrapper)
def findLeftBank(自身,列表):
对于范围内的i(len(列表)):
curr=列表[i]
currenxt=list[i+1]如果i+1当前下一步):
返回i
返回len(列表)-1
def findRightBank(自身、列表、左高):
biggestLoc=len(列表)
最大值=0
对于范围内的i(len(列表)):
如果(列表[i]>=leftHight):
返回i
如果(列表[i]>最大值):
biggestLoc=i
最大=列表[i]
返回biggestLoc
def GetRains(自我,列表):
全部=0
对于范围内的i(len(列表)):
列表=列表[i]
高度=最小值(列表[0],列表[len(列表)-1])
对于范围(1,长度(列表)-1)内的i:
如果(列出[i]>高度):
持续
全部=全部+(高度-列表[i])
全部归还
s=解决方案()
打印s.trap([9,6,8,8,5,6,3])
上面可以吗

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
这是一个基于for问题的单遍(!)(O(n)-时间)O(n)-空间算法:

我已经用蛮力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\u water\u heldover\u 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)
导入系统 从colorama导入后、前、样式、初始化#$pip安装colorama init(strip=not sys.stderr.isatty()) def max_water_heldover_bruteforce(高度): 如果不是高度:返回0 建筑、空气、水=['B','', Back.BLUE+Fore.WHITE+W'+Fore.RESET+Back.RESET+Style.RESET\u ALL] 矩阵=[[水]*范围内的长度(高度)(最大高度))] 对于当前楼层,枚举中的摩天大楼(矩阵,起点=1): 外部=真 对于建筑物编号,枚举中的建筑物高度(高度): 如果当前楼层建筑高度: 摩天大楼[-i]=空气 其他: 打破 如果sys.argv中的“--绘制摩天大楼”: 打印('\n'.join(映射('.join,矩阵[::-1])),文件=sys.stderr) 打印('-'*60,文件=sys.stderr) 返回和(如果单元格==水,则矩阵行中的单元格为1)
结果是相同的。

这里有一个单程解决方案,它改进了刘志东和J.s.Sebastian的解决方案,只使用O(1)空间:

双通道解决方案基于以下逻辑:

  • 找到m
    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)
    
    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
    
    >>> rands = [random.randrange(0, 1000000) for i in xrange(10000000)]
    >>> %timeit fillcount(rands)
    1 loops, best of 3: 3.3 s per loop
    
    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
    
    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