Вода, собранная между башнями

Недавно я наткнулся на вопрос интервью, заданный Amazon, и я не могу найти оптимизированный алгоритм для решения этого вопроса:

Вам предоставляется входной массив, каждый элемент которого представляет высоту строчных башен. Ширина каждой башни равна 1. Начинается дождь. Сколько воды собирается между башнями?

Пример

Input: [1,5,3,7,2] , Output: 2 units
Explanation: 2 units of water collected between towers of height 5 and 7

   *
   *
 *w*
 *w*
 ***
 ****
*****

Другой пример

Input: [5,3,7,2,6,4,5,9,1,2] , Output: 14 units 
Explanation= 2 units of water collected between towers of height 5 and 7 +
             4 units of water collected between towers of height 7 and 6 + 
             1 units of water collected between towers of height 6 and 5 +
             2 units of water collected between towers of height 6 and 9 +
             4 units of water collected between towers of height 7 and 9 +
             1 units of water collected between towers of height 9 and 2.

Сначала я думал, что это может быть решено с помощью проблемы запаса (http://www.geeksforgeeks.org/the-stock-span-problem/), но я был не прав, поэтому было бы здорово, если бы любой может подумать об алгоритме, оптимизированном по времени для этого вопроса.

Ответ 1

Как только вода будет падать, каждая позиция будет заполняться до уровня, равного меньшей по высоте башни слева и самой высокой башни справа.

Найдите, по правильному просмотру, самую высокую башню слева от каждой позиции. Затем найдите с помощью сканирования слева самую высокую башню справа от каждой позиции. Затем возьмите минимум в каждой позиции и добавьте все.

Что-то вроде этого должно работать:

int tow[N]; // nonnegative tower heights
int hl[N] = {0}, hr[N] = {0}; // highest-left and highest-right
for (int i = 0; i < n; i++) hl[i] = max(tow[i], (i!=0)?hl[i-1]:0);
for (int i = n-1; i >= 0; i--) hr[i] = max(tow[i],i<(n-1) ? hr[i+1]:0);
int ans = 0;
for (int i = 0; i < n; i++) ans += min(hl[i], hr[i]) - tow[i];

Ответ 2

Здесь эффективное решение в Haskell

rainfall :: [Int] -> Int
rainfall xs = sum (zipWith (-) mins xs)
    where mins = zipWith min maxl maxr
          maxl = scanl1 max xs
          maxr = scanr1 max xs

он использует тот же алгоритм двухпроходного сканирования, упомянутый в других ответах.

Ответ 3

Обратитесь на этот сайт за кодом, его действительно простой и простой http://learningarsenal.info/index.php/2015/08/21/amount-of-rain-water-collected-between-towers/

Вход: [5,3,7,2,6,4,5,9,1,2], Выход: 14 единиц

введите описание изображения здесь Объяснение

Каждая башня может удерживать воду до уровня наименьшей высоты между самой высокой башней налево и самой высокой башней справа.

Таким образом, нам нужно вычислить высоту башни слева на каждой башне, а также для правой стороны.

Здесь нам понадобятся два дополнительных массива для размещения высоты самой высокой башни слева на любой башне, например, int leftMax [], а также для правой стороны: int rightMax [].

ШАГ-1

Мы делаем левый проход данного массива (т.е. int tower []) и будем поддерживать временный максимум (например, int tempMax), так что на каждой высоте итерации каждой башни будет сравниваться с tempMax, а если высота текущей башни меньше, чем tempMax, тогда tempMax будет установлен как самая высокая башня слева от нее, в противном случае высота текущей башни будет назначена как самая большая башня слева, а tempMax будет обновляться с текущей высотой башни,

ШАГ-2

Мы будем следовать процедуре выше, только как описано в STEP-1, чтобы вычислить высоту башни справа, но это время делает проход через массив с правой стороны.

ЭТАП-3

Количество воды, которое может выдерживать каждая башня, составляет

(минимальная высота между верхней правой башней и самой высокой левой башней) - (высота башни)

Ответ 4

Вы можете сделать это, дважды просмотрев массив.

Первый раз, когда вы просматриваете сверху вниз и сохраняете значение самой высокой башни, с которой вы еще не сталкивались при достижении каждой строки.

Затем вы повторяете процесс, но наоборот. Вы начинаете снизу и работаете в верхней части массива. Вы следите за самой высокой башней, которую вы видели до сих пор, и сравните ее высоту со значением для этой башни в другом результирующем наборе.

Возьмите разницу между меньшим из этих двух значений (самой короткой из самых высоких двух башен, окружающих текущую башню, вычтите высоту башни и добавьте ее к общему количеству воды.

int maxValue = 0;
int total = 0;
int[n] lookAhead

for(i=0;i<n;i++)
{
    if(input[i] > maxValue) maxValue = input[i];
    lookahead[i] = maxValue;
}

maxValue = 0;
for(i=n-1;i>=0;i--)
{
    // If the input is greater than or equal to the max, all water escapes.
    if(input[i] >= maxValue)
    { 
        maxValue = input[i];
    }
    else
    {
        if(maxValue > lookAhead[i])
        {
            // Make sure we don't run off the other side.
            if(lookAhead[i] > input[i])
            {
                total += lookAhead[i] - input[i];
            }
        }
        else
        {
            total += maxValue - input[i];
        }
    } 
}

Ответ 5

Считываемое решение для Python:

def water_collected(heights):
    water_collected = 0
    left_height = []
    right_height = []

    temp_max = heights[0]
    for height in heights:
        if (height > temp_max):
            temp_max = height
        left_height.append(temp_max)

    temp_max = heights[-1]
    for height in reversed(heights):
        if (height > temp_max):
            temp_max = height
        right_height.insert(0, temp_max)

    for i, height in enumerate(heights):
        water_collected += min(left_height[i], right_height[i]) - height
    return water_collected

Ответ 6

O (n) решение в Java, однопроходное

Другая реализация на Java, нахождение воды, собранной за один проход через список. Я просмотрел другие ответы, но не видел никаких, которые, очевидно, использовали мое решение.

  • Найдите первый "пик", пройдя по списку, пока высота башни не перестанет расти. Вся вода до этого не будет собрана (слить влево).
  • Для всех последующих башен:
    • Если высота последующей башни уменьшается или остается неизменной, добавьте воду в ковш "потенциального сбора", равный разности между высотой башни и предыдущей максимальной высотой башни.
    • Если высота последующей башни увеличивается, мы собираем воду из предыдущего ковша (вычитаем из ковша потенциальной коллекции и добавляем в собранный ведро), а также добавляем воду в потенциальный ковш, равный разности между башней высота и предыдущая максимальная высота башни.
    • Если мы найдем новую максимальную башню, тогда вся "потенциальная вода" будет перемещена в собранный ковш, и это станет новой максимальной высотой башни.

В приведенном выше примере с вводом: [5,3,7,2,6,4,5,9,1,2] решение работает следующим образом:

  • 5: Находит 5 как первый пик
  • 3: добавляет 2 к потенциальному ковшу (5-3)

    собрано = 0, потенциал = 2

  • 7: Новый максимум, перемещает всю потенциальную воду в собранный ведро

    собран = 2, потенциал = 0

  • 2: добавляет 5 в потенциальный ковш (7-2)

    собран = 2, потенциал = 5

  • 6: перемещает 4 в собранный ведро и добавляет 1 в потенциальный ковш (6-2, 7-6)

    собран = 6, потенциал = 2

  • 4: добавляет 2 к потенциальному ковшу (6-4)

    собрано = 6, потенциал = 4

  • 5: перемещает 1 в собранный ведро и добавляет 2 к потенциальному ковшу (5-4, 7-5)

    собран = 7, потенциал = 6

  • 9: Новый максимум, перемещает всю потенциальную воду в собранный ведро

    собрано = 13, потенциал = 0

  • 1: добавляет 8 к потенциальному ковшу (9-1)

    собрано = 13, потенциал = 8

  • 2: перемещает 1 в собранный ведро и добавляет 7 в потенциальный ковш (2-1, 9-2)

    собран = 14, потенциал = 15

После прохода по списку один раз была измерена собранная вода.

public static int answer(int[] list) {
    int maxHeight = 0;
    int previousHeight = 0;
    int previousHeightIndex = 0;
    int coll = 0;
    int temp = 0;

    // find the first peak (all water before will not be collected)
    while(list[previousHeightIndex] > maxHeight) {
        maxHeight = list[previousHeightIndex];
        previousHeightIndex++;
        if(previousHeightIndex==list.length)            // in case of stairs (no water collected)
            return coll;
        else
            previousHeight = list[previousHeightIndex];
    }

    for(int i = previousHeightIndex; i<list.length; i++) {
        if(list[i] >= maxHeight) {      // collect all temp water
            coll += temp;
            temp = 0;
            maxHeight = list[i];        // new max height
        }
        else {
            temp += maxHeight - list[i];
            if(list[i] > previousHeight) {  // we went up... collect some water
                int collWater = (i-previousHeightIndex)*(list[i]-previousHeight);
                coll += collWater;
                temp -= collWater;
            }
        }

        // previousHeight only changes if consecutive towers are not same height
        if(list[i] != previousHeight) {
            previousHeight = list[i];
            previousHeightIndex = i;
        }
    }
    return coll;
}

Ответ 7

Ни один из 17 уже опубликованных ответов не является оптимальным по времени.

Для одного процессора выборка 2 развертки (left->right, за которой следует суммирование right->left) является оптимальной, как указывали многие люди, но с использованием многих процессоров можно выполнить эту задачу в O (log n). Существует много способов сделать это, поэтому я объясню тот, который достаточно близок к последовательному алгоритму.

Максимальное кэшированное дерево O (log n)

1: Создайте двоичное дерево всех башен, чтобы каждый node содержал высоту самой высокой башни у любого из ее детей. Поскольку два листа любого node могут быть вычислены независимо, это можно сделать в O(log n) времени с n cpu's.

2a: Затем для каждого node в дереве, начиная с корня, пусть правый лист имеет значение max(left, self, right). Это создаст монотонную развертку слева направо в O(log n), используя n cpu.

2b: Чтобы вычислить прокрутку справа налево, мы выполняем ту же процедуру, что и раньше. Начиная с корня дерева с максимальным кэшем, пусть левый лист имеет значение max(left, self, right). Эти стрелки слева направо (2a) и справа налево (2b) могут выполняться параллельно, если вы хотите. Они оба используют максимальное кэшированное дерево в качестве входных данных и генерируют одно новое дерево каждый (или задают свои собственные поля в исходном дереве, если вы предпочитаете это).

3: Тогда для каждой башни количество воды на ней min(ltr, rtl) - towerHeight, где ltr - значение этой башни в монотонной развертке слева направо, которую мы делали раньше, то есть максимальная высота любая башня слева от нас (включая нас 1), а rtl одинакова для стрелки справа налево.

4: просто суммируйте это, используя дерево в O(log n), используя n cpu, и мы закончили.


1 Если текущая башня выше всех башен слева от нас или выше всех башен справа от нас, min(ltr, rtl) - towerHeight равна нулю.

Здесь два других способа сделать это.

Ответ 8

Вы можете перемещаться сначала слева направо и вычислять воду, накопленную для случаев, когда слева расположено меньшее здание, а другое справа. Вы должны были бы вычесть площадь зданий, находящихся между этими двумя зданиями, и меньше левого.

Аналогично было бы право налево.

Вот код слева направо. Я загрузил эту проблему в онлайн-судьи на основе leetcode, используя этот подход.

Я нахожу этот подход более интуитивным, чем стандартное решение, которое присутствует везде (вычисление самого большого здания справа и слева для каждого i).

int sum=0, finalAns=0;
    idx=0;
    while(a[idx]==0 && idx < n)
        idx++;
    for(int i=idx+1;i<n;i++){

        while(a[i] < a[idx] && i<n){
            sum += a[i];
            i++;
        }
        if(i==n)
            break;
        jdx=i;
        int area = a[idx] * (jdx-idx-1);
        area -= sum;
        finalAns += area;

        idx=jdx;
        sum=0;
    }

Временная сложность этого подхода - O (n), так как вы перемещаете массив два раза линейно. Сложность пространства была бы O (1).

Ответ 9

Вот решение в Groovy за два прохода.

assert waterCollected([1, 5, 3, 7, 2]) == 2
assert waterCollected([5, 3, 7, 2, 6, 4, 5, 9, 1, 2]) == 14
assert waterCollected([5, 5, 5, 5]) == 0
assert waterCollected([5, 6, 7, 8]) == 0
assert waterCollected([8, 7, 7, 6]) == 0
assert waterCollected([6, 7, 10, 7, 6]) == 0

def waterCollected(towers) {
    int size = towers.size()
    if (size < 3) return 0

    int left = towers[0]
    int right = towers[towers.size() - 1]

    def highestToTheLeft = []
    def highestToTheRight = [null] * size

    for (int i = 1; i < size; i++) {

        // Track highest tower to the left
        if (towers[i] < left) {
            highestToTheLeft[i] = left
        } else {
            left = towers[i]
        }

        // Track highest tower to the right
        if (towers[size - 1 - i] < right) {
            highestToTheRight[size - 1 - i] = right
        } else {
            right = towers[size - 1 - i]
        }
    }

    int water = 0
    for (int i = 0; i < size; i++) {
        if (highestToTheLeft[i] && highestToTheRight[i]) {
            int minHighest = highestToTheLeft[i] < highestToTheRight[i] ? highestToTheLeft[i] : highestToTheRight[i]
            water += minHighest - towers[i]
        }
    }

    return water
}

Здесь же фрагмент с онлайн-компилятором: https://groovy-playground.appspot.com/#?load=3b1d964bfd66dc623c89

Ответ 10

Первый и последний бары в списке не могут ловить воду. Для оставшихся башен они могут ловить воду, когда есть максимальные высоты слева и справа.

Накопление воды: max (min (max_left, max_right) - current_height, 0)

Итерация слева, если мы знаем, что max_right больше, min (max_left, max_right) станет просто max_left. Поэтому накопление воды упрощается: max (max_left - current_height, 0) Тот же шаблон при рассмотрении с правой стороны.

Из вышеприведенной информации мы можем написать алгоритм O (N) и O (1) пространства как следующие (в Python):

def trap_water(A):

     water = 0
     left, right = 1, len(A)-1
     max_left, max_right = A[0], A[len(A)-1]

     while left <= right:
         if A[left] <= A[right]:
             max_left = max(A[left], max_left)
             water += max(max_left - A[left], 0)
             left += 1
         else:
             max_right = max(A[right], max_right)
             water += max(max_right - A[right], 0)
             right -= 1

     return water

Ответ 11

Мой уродливый одиночный обходной спан

def water_collection(buildings):
  valleyFlag = False
  water = 0
  pool = []
  for i, building in enumerate(buildings):
    if(i == 0):
      lastHill = building
    else:
      if lastHill <= building or i == len(buildings)-1:
        minHill = min(building, lastHill)
        print("Hill {} to Hill {}".format(lastHill, building))
        summ = 0
        for drop in pool:
          summ += minHill - drop
          water += minHill - drop
        print("Collected sum {}".format(summ))
        pool = []
        valleyFlag = False
        lastHill = building
      elif lastHill > building and valleyFlag == False:
        pool.append(building)
        valleyFlag = True
      elif lastHill > building and valleyFlag == True:
        pool.append(building)

  print(water)

Ответ 12

Вот еще одно решение, написанное на Scala

def find(a: Array[Int]): Int = {
  var count, left, right = 0

  while (left < a.length - 1) {
    right = a.length - 1
    for (j <- a.length - 1 until left by -1) {
      if (a(j) > a(right)) right = j
    }

    if (right - left > 1) {
      for (k <- left + 1 until right) count += math.min(a(left), a(right)) - a(k)
      left = right
    } else left += 1
  }

  count
}

Ответ 13

Альтернативный алгоритм в стиле Euclid, который я считаю более элегантным, чем все это сканирование:

Установите две самые высокие башни как левую и правую башню. Количество вода, содержащаяся между этими башнями, очевидна.

Возьмите следующую самую высокую башню и добавьте ее. Это должно быть либо между концевые башни, или нет. Если он находится между концевыми башнями, он вытесняет количество воды, равное объему башен (спасибо Архимеду за этот намек). Если он находится за пределами башни, он становится новой торцевой башней и количество дополнительной воды, содержащейся, очевидно.

Повторите для следующей самой высокой башни, пока не будут добавлены все башни.

Я написал код для этого (в современной евклидовой идиоме) здесь: http://www.rosettacode.org/wiki/Water_collected_between_towers#F.23

Ответ 14

Я написал это, опираясь на некоторые из вышеперечисленных идей в этой теме:

def get_collected_rain(towers):

    length = len(towers)
    acummulated_water=[0]*length
    left_max=[0]*length
    right_max=[0]*length

    for n in range(0,length):

        #first left item
        if n!=0:
            left_max[n]=max(towers[:n])

        #first right item
        if n!=length-1:
            right_max[n]=max(towers[n+1:length])

        acummulated_water[n]=max(min(left_max[n], right_max[n]) - towers[n], 0)

    return sum(acummulated_water)

Что ж...

> print(get_collected_rain([9,8,7,8,9,5,6]))

> 5

Ответ 15

У меня есть решение, которое требует только одного обхода слева направо.

def standing_water(heights):

    if len(heights) < 3:
        return 0

    i = 0   # index used to iterate from left to right
    w = 0   # accumulator for the total amount of water

    while i < len(heights) - 1:

        target = i + 1
        for j in range(i + 1, len(heights)):

            if heights[j] >= heights[i]:
                target = j
                break

            if heights[j] > heights[target]:
                target = j

        if target == i:
            return w

        surface = min(heights[i], heights[target])

        i += 1

        while i < target:
            w += surface - heights[i]
            i += 1

    return w

Ответ 16

Здесь моя попытка в jQuery. Он только сканирует вправо.

Рабочая скрипка (с полезным протоколированием)

var a = [1, 5, 3, 7, 2];
var water = 0;

$.each(a, function (key, i) {
  if (i > a[key + 1]) { //if next tower to right is bigger
      for (j = 1; j <= a.length - key; j++) { //number of remaining towers to the right
          if (a[key+1 + j] >= i) { //if any tower to the right is bigger
              for (k = 1; k < 1+j; k++) {
              //add to water: the difference of the first tower and each tower between the first tower and its bigger tower
                  water += a[key] - a[key+k];
              }
          }
      }
  }
});

console.log("Water: "+water);

Ответ 17

Вот иди сюда на Python. Довольно уверен, что он работает, но не проверял его.

Два прохода по списку (но удаление списка, когда он находит "воду" ):

def answer (высоты):

def accWater(lst,sumwater=0):
    x,takewater = 1,[]
    while x < len(lst):
        a,b = lst[x-1],lst[x]
        if takewater:
            if b < takewater[0]:
                takewater.append(b)
                x += 1
            else:
                sumwater += sum(takewater[0]- z for z in takewater)
                del lst[:x]
                x = 1
                takewater = []
        else:
            if b < a:
                takewater.extend([a,b])
                x += 1
            else:
                x += 1
    return [lst,sumwater]

heights, swater = accWater(heights)
x, allwater = accWater(heights[::-1],sumwater=swater)
return allwater

Ответ 18

Интуитивное решение этой проблемы - это тот, в котором вы связали проблему и заполнили воду на основе высоты левой и правой границ.

Мое решение:

  • Начните с левой стороны, установив обе границы как 0-й индекс.
  • Проверьте и посмотрите, есть ли какая-то траектория (если вы были прогуляйтесь по вершинам этих башен, вы когда-нибудь спуститесь вниз, а затем снова?) Если это так, то вы нашли правую границу.
  • Теперь вернитесь назад и заполните воду соответственно (я просто добавил вода к массиву сама по себе, так как она делает код немного чище, но это, очевидно, не требуется).
  • Линия перфорации: Если высота левой ограничивающей башни больше, чем высота правой ограничительной башни, чем вам нужно увеличить правую связаны. Причина в том, что вы можете столкнуться с более высокой башней и заполнить еще немного воды. Однако, если правая башня выше левой башни, тогда нет в вашей текущей проблеме может быть добавлено больше воды. Таким образом, вы перемещаетесь ваши левые связаны с правой границей и продолжаются.

Вот реализация в С#:

        int[] towers = {1,5,3,7,2};

        int currentMinimum = towers[0];

        bool rightBoundFound = false;

        int i = 0;
        int leftBoundIndex = 0;
        int rightBoundIndex = 0;

        int waterAdded = 0;

        while(i < towers.Length - 1)
        {

            currentMinimum = towers[i];

            if(towers[i] < currentMinimum)
            {
                currentMinimum = towers[i];
            }

            if(towers[i + 1] > towers[i])
            {
                rightBoundFound = true;
                rightBoundIndex = i + 1;
            }

            if (rightBoundFound)
            {

                for(int j = leftBoundIndex + 1; j < rightBoundIndex; j++)
                {

                    int difference = 0;

                    if(towers[leftBoundIndex] < towers[rightBoundIndex])
                    {
                        difference = towers[leftBoundIndex] - towers[j];
                    }
                    else if(towers[leftBoundIndex] > towers[rightBoundIndex])
                    {
                        difference = towers[rightBoundIndex] - towers[j];
                    }
                    else
                    {
                        difference = towers[rightBoundIndex] - towers[j];
                    }

                    towers[j] += difference;
                    waterAdded += difference;

                }

                if (towers[leftBoundIndex] > towers[rightBoundIndex])
                {
                    i = leftBoundIndex - 1;
                }
                else if (towers[rightBoundIndex] > towers[leftBoundIndex])
                {
                    leftBoundIndex = rightBoundIndex;
                    i = rightBoundIndex - 1;
                }
                else
                {
                    leftBoundIndex = rightBoundIndex;
                    i = rightBoundIndex - 1;
                }
                rightBoundFound = false;

            }

            i++;

        }

Я не сомневаюсь, что есть более оптимальные решения. В настоящее время я работаю над однопроходной оптимизацией. Существует также очень аккуратная реализация стека этой проблемы, и она использует аналогичную идею ограничения.

Ответ 19

Вот мое решение, оно проходит этот уровень и довольно быстро, легко понять Идея очень проста: во-первых, вы вычисляете максимальную высоту (она может быть кратной максимальной), затем вы разбиваете пейзаж на 3 части, от начала до левого самого максимального уровня, между левым максимумом до right max max и справа от max до конца.

В средней части легко собирать дожди, один для цикла делает это. Затем для первой части вы продолжаете обновлять текущую максимальную высоту, которая меньше максимальной высоты ландшафта. один цикл делает это. Затем для третьей части вы отмените то, что вы сделали с первой частью

def answer(heights):
    sumL = 0
    sumM = 0
    sumR = 0
    L = len(heights)
    MV = max(heights)
    FI = heights.index(MV)
    LI = L - heights[::-1].index(MV) - 1
    if LI-FI>1:
        for i in range(FI+1,LI):
            sumM = sumM + MV-heights[i]

    if FI>0:
        TM = heights[0]
        for i in range(1,FI):
            if heights[i]<= TM:
                sumL = sumL + TM-heights[i]
            else:
                TM = heights[i]
    if LI<(L-1):
        TM = heights[-1]
        for i in range(L-1,LI,-1):
            if heights[i]<= TM:
               sumL = sumL + TM-heights[i]
            else:
               TM = heights[i]
    return(sumL+sumM+sumR)        

Ответ 20

Вот решение в JAVA, которое перебирает список чисел один раз. Таким образом, самым худшим случаем является O (n). (По крайней мере, как я это понимаю).

Для данного ссылочного номера продолжайте искать число, большее или равное ссылочному номеру. Держите подсчет чисел, пройденных при этом, и сохраните все эти числа в списке.

Идея такова. Если есть 5 чисел между 6 и 9, и все пять чисел равны 0, это означает, что в общей сложности 30 единиц воды могут храниться между 6 и 9. Для реальной ситуации, когда числа между ними не равны 0, мы просто вычитаем общую сумму чисел между общей суммой, если эти числа равны 0. (В этом случае мы вычитаем из 30). И это даст счет воды, хранящейся между этими двумя башнями. Затем мы сохраняем эту сумму в переменной totalWaterRetained, а затем начинаем со следующей башни после 9 и продолжаем делать то же самое до последнего элемента.

Добавление всех экземпляров totalWaterRetained даст нам окончательный ответ.

Решение JAVA: (Протестировано на нескольких входах. Может быть не на 100% правильно)

private static int solveLineTowerProblem(int[] inputArray) {
    int totalWaterContained = 0;
    int index;
    int currentIndex = 0;
    int countInBetween = 0;
    List<Integer> integerList = new ArrayList<Integer>();

    if (inputArray.length < 3) {
        return totalWaterContained;
    } else {
        for (index = 1; index < inputArray.length - 1;) {
            countInBetween = 0;
            integerList.clear();

            int tempIndex = index;
            boolean flag = false;

            while (inputArray[currentIndex] > inputArray[tempIndex] && tempIndex < inputArray.length - 1) {
                integerList.add(inputArray[tempIndex]);
                tempIndex++;
                countInBetween++;
                flag = true;
            }

            if (flag) {
                integerList.add(inputArray[index + countInBetween]);
                integerList.add(inputArray[index - 1]);

                int differnceBetweenHighest = min(integerList.get(integerList.size() - 2),
                        integerList.get(integerList.size() - 1));
                int totalCapacity = differnceBetweenHighest * countInBetween;
                totalWaterContained += totalCapacity - sum(integerList);
            }
            index += countInBetween + 1;
            currentIndex = index - 1;
        }
    }
    return totalWaterContained;
}

Ответ 21

Программа JavaScript для поиска общей воды:

let buildingHeights = [6, 1, 3, 5, 9, 2, 8];
/*
 * TOTAL store water
 * */
let max = (n1, n2) => {
    return n1 > n2 ? n1 : n2;
};
let min = (n1, n2) => {
    return n1 > n2 ? n2 : n1;
};

let maxHeightFromLeft = {}, maxHeightFromRight = {};
for (let i = 0; i < buildingHeights.length; i++) {
    maxHeightFromLeft[i] = max(buildingHeights[i], (i != 0) ? maxHeightFromLeft[i - 1] : 0);
}
for (let i = buildingHeights.length - 1; i >= 0; i--) {
    maxHeightFromRight[i] = max(buildingHeights[i], i < (buildingHeights.length - 1) ? maxHeightFromRight[i + 1] : 0);
}
let totalStorage = 0;
for (let i = 0; i < buildingHeights.length; i++) {
    totalStorage += min(maxHeightFromLeft[i], maxHeightFromRight[i]) - buildingHeights[i];
}
console.log(totalStorage);

Ответ 22

private static int soln1(int[] a)
    {
        int ret=0;
        int l=a.length;
        int st,en=0;
        int h,i,j,k=0;
        int sm;
        for(h=0;h<l;h++)
        {
        for(i=1;i<l;i++)
        {
            if(a[i]<a[i-1])
            {
                st=i;
                for(j=i;j<l-1;j++)
                {
                    if(a[j]<=a[i] && a[j+1]>a[i])
                    {
                        en=j;
                        h=en;
                        break;
                    }
                }
                if(st<=en)
                {
                    sm=a[st-1];
                    if(sm>a[en+1])
                        sm=a[en+1];
                    for(k=st;k<=en;k++)
                    {
                        ret+=sm-a[k];
                        a[k]=sm;
                    }
                }
            }
        }
        }
        return ret;
    }

Ответ 23

Вот мой подход к проблеме, Я использую цикл, чтобы увидеть, больше ли предыдущие башни, чем фактические. Если это так, я создаю еще один цикл, чтобы проверить, больше ли башни, идущие после фактического, больше или равны предыдущей башне. Если это случай, то я просто добавляю все различия в высоте между предыдущей башней и всеми другими башнями. Если нет, и если мой цикл достигает моего последнего объекта, я просто меняю массив так, чтобы предыдущая башня стала моей последней башней и вызывала мой метод рекурсивно. Таким образом, я уверен, что вы найдете башню больше, чем моя новая башня, и найдет нужное количество воды.

public class towers {
    public static int waterLevel(int[] i) {
        int totalLevel = 0;

        for (int j = 1; j < i.length - 1; j++) {
            if (i[j - 1] > i[j]) {
                for (int k = j; k < i.length; k++) {
                    if (i[k] >= i[j  - 1]) {
                        for (int l = j; l < k; l++) { 
                            totalLevel += (i[j - 1] - i[l]);
                        }

                        j = k;
                        break;
                    }  

                    if (k == i.length - 1) {
                        int[] copy = Arrays.copyOfRange(i, j - 1, k + 1);
                        int[] revcopy = reverse(copy);
                        totalLevel += waterLevel(revcopy);
                    }
                }
            }
        }

        return totalLevel;
    }

    public static int[] reverse(int[] i) {
        for (int j = 0; j < i.length / 2; j++) {
            int temp = i[j];
            i[j] = i[i.length - j - 1];
            i[i.length - j - 1] = temp;
        }

        return i;
    }

    public static void main(String[] args) {
        System.out.println(waterLevel(new int[] {1, 6, 3, 2, 2, 6}));
    }
}

Ответ 24

Протестировано все предоставленное Java-решение, но ни одна из них не пропускает даже половину тестовых сценариев, которые я придумал, поэтому существует еще одно решение Java O (n), при этом все возможные случаи. Алгоритм очень прост:

1) Поверните вход с начала, ищите башню, которая равна или выше этой башни, суммируя возможное количество воды для нижних башен во временный var.

2) Как только найденная башня - добавьте этот временный var в основной результат var и сократите список ввода.

3) Если башня не найдена, то переверните оставшийся вход и снова просчитайте.

public int calculate(List<Integer> input) {
    int result = doCalculation(input);
    Collections.reverse(input);
    result += doCalculation(input);
    return result;
}

private static int doCalculation(List<Integer> input) {
    List<Integer> copy = new ArrayList<>(input);
    int result = 0;
    for (ListIterator<Integer> iterator = input.listIterator(); iterator.hasNext(); ) {
        final int firstHill = iterator.next();
        int tempResult = 0;
        int lowerHillsSize = 0;
        while (iterator.hasNext()) {
            final int nextHill = iterator.next();
            if (nextHill >= firstHill) {
                iterator.previous();
                result += tempResult;
                copy = copy.subList(lowerHillsSize + 1, copy.size());
                break;
            } else {
                tempResult += firstHill - nextHill;
                lowerHillsSize++;
            }
        }
    }
    input.clear();
    input.addAll(copy);
    return result;
}

Для тестовых примеров, пожалуйста, ознакомьтесь с этим классом тестирования.

Не стесняйтесь создавать запрос на перенос, если вы обнаружите непокрытые тестовые примеры)

Ответ 25

Это смешная проблема, я просто получил этот вопрос в интервью. LOL Я сломал себе голову на эту глупую проблему и нашел решение, которое требует одного прохода (но явно не непрерывного). (и на самом деле вы даже не зацикливаете на все данные, поскольку вы обходите границу...)

Итак, идея такова. Вы начинаете со стороны с самой низкой башни (которая теперь является эталоном). Вы напрямую добавляете содержимое башен, и если вы достигаете башни, которая является самой высокой, чем эталон, вы вызываете функцию рекурсивно (с боком, подлежащей сбросу). Не тривиально объяснять словами, код говорит сам за себя.

  #include <iostream>
  using namespace std;

  int compute_water(int * array, int index_min, int index_max)
  {
  int water = 0;
  int dir;
  int start,end;
  int steps = std::abs(index_max-index_min)-1;
  int i,count;
  if(steps>=1)
  {
    if(array[index_min]<array[index_max])
    {
      dir=1;
      start = index_min;
      end = index_max;
    }
    else
    {
      dir = -1;
      start = index_max;
      end = index_min;
    }
    for(i=start+dir,count=0;count<steps;i+=dir,count++)
    {
      if(array[i]<=array[start])water += array[start] - array[i];
      else
      {
        if(i<end)water += compute_water(array, i, end);
        else water += compute_water(array, end, i);
        break;
      }
    }
  }
  return water;
}

int main(int argc,char ** argv)
{
  int size = 0;
  int * towers;
  if(argc==1)
  {
    cout<< "Usage: "<<argv[0]<< "a list of tower height separated by spaces" <<endl;
  }
  else
  {
    size = argc - 1;
    towers =  (int*)malloc(size*sizeof(int));
    for(int i = 0; i<size;i++)towers[i] = atoi(argv[i+1]);
    cout<< "water collected: "<< compute_water(towers, 0, size-1)<<endl;
    free(towers);
  }
}