一尘不染

在给定建筑物高度的情况下解决积水的算法

algorithm

我正在练习算法,而我在这个问题上已经停留了几天。当我测试解决方案时,我仍然不正确。这是问题说明:

纽约华尔街以其惊人的摩天大楼而闻名。但是雨季即将来临,今年将要洒落在建筑物上的水量将会很大。由于每座建筑物都固定在其左右两侧的建筑物上(第一个和最后一个除外),因此,只有当建筑物的高度高于建筑物的高度时,水才可能从建筑物漏水。向左或向右(华尔街边缘的高度为0)。所有建筑物的宽度均为1米。您将获得从左到右的华尔街建筑物的高度(以米为单位),并且您的任务是将华尔街建筑物上所保持的总水量(以立方米为单位)打印到标准输出(标准输出)

输入示例:

heights: [9 8 7 8 9 5 6]

输出示例:

5

说明:
在此示例中,在第一座和第五座建筑物之间有4立方米的水(第二座是1立方米,第二座是3立方米,第四座是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

阅读 298

收藏
2020-07-28

共1个答案

一尘不染

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

这是一种基于堆栈的解决方案的单遍(!)(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)

结果是相同的。

2020-07-28