Настольная игра: найдите максимум зеленых точек с ограниченным количеством красных - PullRequest
9 голосов
/ 01 июля 2019

Сценарий:

  • На шахматной доске размером MxN есть красные и зеленые фигуры.

  • Каждый квадрат на доске может содержать любое количество фигур любого цвета. (у нас может быть 5 штук - 3 зеленых и 2 красных в одном квадрате, или, например, зеленый, зеленый, или красный красный, или любое другое число)

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

  • Тем не менее, прямоугольник может содержать не более определенного количества красных фигур.

  • Один угол прямоугольника должен быть (0,0).

Пример:

Размер доски 6х4, красные фигуры отмечены «х», зеленые фигуры отмечены «о».

      +-+-+-+-+-+-+
    3 | | | |o|x|o|
      +-+-+-+-+-+-+
    2 |o| |x| | |o|
      +-+-+-+-+-+-+
    1 |o| |o| |o|x|
      +-+-+-+-+-+-+
    0 | |o| |x| |x|
      +-+-+-+-+-+-+
       0 1 2 3 4 5
  • Если мы допустим 2 красных фрагмента, то (4,2) является оптимальным решением, поскольку область между (0,0) и (4,2) содержит 5 зеленых фрагментов и 2 красных фрагмента. Никакая точка с 2 красными фигурами не содержит более 5 зеленых фигур (3,3) также является оптимальным решением.

  • Если мы допустим 3 красных кусочка, то (4,3) является единственным оптимальным решением с 6 зелеными кусочками.

Мне дали:

  • размер платы,
  • координаты всех зеленых и красных фигур,
  • количество разрешенных красных кусочков («maxRed»)

Цель: Для любого заданного «maxRed» класс должен иметь возможность вычислять координаты (x, y) так, чтобы выровненный по оси прямоугольник между (0,0) и (x, y) содержал не более «maxRed» красных частей, и количество зеленых штук максимально.

Мой вопрос:

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

Немного интуиции:

Я подумал, глядя на максимальные красные точки шкафа, которые могут находиться в прямоугольнике (если maxRed = 2, затем на ближайшие две красные точки) от начала координат (0,0), а затем проверил все возможные «расширения» прямоугольник от этих точек (только ширина, только высота, ширина и высота и т. д.), что, на мой взгляд, не так эффективно (в худшем случае это неэффективно).

Тогда я подумал, что, может быть, если maxRed равно 2, и мы нашли две ближайшие красные точки, то мы можем проверить, где находится 3-й maxRed (если его не существует, вернуть всю матрицу в виде прямоугольника), и каким-то образом искать ' less 'количество опций - все равно нужно подумать обо всех случаях (3-я точка может быть сверху текущего прямоугольника, или слева от него, или по диагонали), и если это, например, в верхней части, тогда может быть случай, что я могу расширить его по ширине - а может и нет (потому что, возможно, есть еще красные точки справа от него). но вы понимаете, как-то найти алгоритм, который не является грубым и максимально эффективным.

Вопрос 2: Еще один интересный вопрос, к которому я хотел бы обратиться: Как бы вы решили проблему, если бы координаты были определены как «плавающие», то есть на доске нет сетки? Теперь вам необходимо вернуть наилучшие координаты с плавающей точкой (x, y), чтобы область между (0,0) и (x, y) содержала не более "maxRed" красных фигур, а количество зеленых фигур было максимальным. Как бы вы его нашли, и какова была бы сложность?

Случай 1, например: enter image description here

Случай 2: enter image description here

Еще одна глубокая интуиция:

Крайние случаи: если красные точки на карте меньше 2, вернуть прямоугольник всей матрицы.

  1. Затем мы начнем с создания нашего прямоугольника, закрывающего шкаф двумя красными точками. (например, наш прямоугольник расширится до y = 3, а x = 2) охватит всю эту область.

  2. Затем мы проверяем, какая ось у шкафа у наших красных точек больше, чем у нашего текущего прямоугольника у (у = 3), и какая у у шкафа у наших красных точек больше, чем у наспрямоугольник x (то есть x = 2), шкаф x и y также может приходить из одной и той же 3-й красной точки, он не обязательно должен быть из двух разных красных точек.

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

  4. Затем, на шаге 3, мы попытаемся итеративно подняться (i + 1), если это возможно, и проверитьвсе расширения j, затем перейдите i + 2 и проверьте все j ..

4.1, затем, если возможно, идите направо (j + 1), и, конечно, проверьте все i,затем продолжайте идти j + 2 и так далее ..

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

Ноэтого недостаточно,

, потому что есть крайний случай, как в 'Case 2'

, в котором есть 2 красных точки в одномпятно, так что нам нужно будет тщательно проверить, есть ли вторая самая дальняя красная точка (она дальше, если она или х, или у обоих, очевидно, больше, чем первая красная точка в шкафу) имеет другую красную точку, если у нее есть в общей сложности две красныеуказывает на одну и ту же ячейку, затем мы расширяемся до ее x или y - и оттуда продолжаем расширяться вверх / вниз.

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

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

Это решение должно сработать, я думаю, и дать примерно O (1) Сложность времени, я думаю.

Ответы [ 3 ]

7 голосов
/ 01 июля 2019

Если вы считаете, что есть только две целочисленные переменные, i, j с 0 <= i <= M, 0 <= j <= N, вы, вероятно, можете решить это с помощью динамического программирования. Я постараюсь написать это ясно и без движка LaTeX, поэтому, пожалуйста, потерпите меня.

Скажем, что вы создали четыре M * N матрицы целых чисел G, R, V и L. Для каждой точки (i, j), g_ij обозначают количество зеленых фигур на этом квадрате, а r_ij количество красных фигур. v_ij обозначает количество зеленых фигур в прямоугольнике (0, 0) - (i, j) или 0, если количество красных фигур слишком велико, а l_ij обозначает количество красных фигур в прямоугольнике или бесконечность, если исходное значение шло превышать лимит. Если я говорю о значении ячейки, я имею в виду v_ij, предел ячейки эквивалентен l_ij.

Начиная с точки (0, 0), подход к программированию будет следующим:

  1. Дано очко (i, j)
  2. Возможные направления до (i + 1, j) и вправо до (i, j + 1).
  3. Для (i + i, j) количество красных фигур в прямоугольнике, l_(i + 1)j, равно пределу предыдущей ячейки l_ij + количество красных фигур в ряду над прямоугольником, поэтому ячейки * С 1035 * по (i + 1, j). Если вы используете предел l_ij, вам не нужно вычислять сумму (i + 1) * j ячеек за один шаг, а только сумму j + 1 ячеек - j ячеек в строке плюс одно сохраненное значение. То же самое касается расчета v_(i + 1)j, просто используйте сохраненное значение v_ij плюс количество зеленых фигур в верхнем ряду.
  4. Если предельное количество красных фигур превышено, вы можете создать прямоугольник между (i + 1, j) и верхним правым углом (M, N) и назначить предел всех этих ячеек как превышенный - потому что все возможные прямоугольники, которые могут быть сформированы там должен содержать прямоугольник (0, 0) - (i + 1, j) и, следовательно, он должен содержать слишком много красных кусочков. Расчеты для правильного хода очень похожи.
  5. Как только больше не будет неизвестных частей, просто найдите самое высокое значение в V за O (MN), и все готово.

Что касается вашего второго вопроса, возможное приближение будет состоять в том, чтобы сделать шаг размером от 0 до 1 и разделить все значения на этот шаг, а затем округлить, так что (2/3, 7/5) с размером шага 1/10 станет (6, 14). Затем примените алгоритм, используя эти шаги. Вы можете запускать его несколько раз, уменьшая размеры шагов, сохраняя и преобразовывая матрицу V между прогонами, чтобы избежать многих вычислений. Я надеюсь, что это помогло!

ОБНОВЛЕНИЕ : теперь с примером выполнения

Например, в каждой ячейке (x, y) обозначает количество зеленых и красных кусочков соответственно. Рядом с ней находятся матрицы G и R - пустые значения означают 0. Ограничение на количество красных фигур - 3.

                                       G                   R
  +-----+-----+-----+-----+    +---+---+---+---+   +---+---+---+---+ 
3 |(1,2)|     |     |(4,0)|  3 | 1 |   |   | 4 | 3 | 2 |   |   |   | 
  +-----+-----+-----+-----+    +---+---+---+---+   +---+---+---+---+ 
2 |     |(3,1)|     |(1,2)|  2 |   | 3 |   | 1 | 2 |   | 1 |   | 2 | 
  +-----+-----+-----+-----+    +---+---+---+---+   +---+---+---+---+ 
1 |(1,1)|(1,1)|     |     |  1 | 1 | 1 |   |   | 1 | 1 | 1 |   |   | 
  +-----+-----+-----+-----+    +---+---+---+---+   +---+---+---+---+ 
0 |     |(0,1)|(3,1)|(1,1)|  0 |   |   | 3 | 1 | 0 |   | 1 | 1 | 1 | 
  +-----+-----+-----+-----+    +---+---+---+---+   +---+---+---+---+ 
     0     1     2     3         0   1   2   3       0   1   2   3   

Начиная с (0, 0), у нас есть 0 красных и 0 зеленых частей, поэтому V и L выглядят следующим образом:

          V                   L         
  +---+---+---+---+   +---+---+---+---+ 
3 |   |   |   |   | 3 |   |   |   |   | 
  +---+---+---+---+   +---+---+---+---+ 
2 |   |   |   |   | 2 |   |   |   |   | 
  +---+---+---+---+   +---+---+---+---+ 
1 |   |   |   |   | 1 |   |   |   |   | 
  +---+---+---+---+   +---+---+---+---+ 
0 | 0 |   |   |   | 0 | 0 |   |   |   | 
  +---+---+---+---+   +---+---+---+---+ 
    0   1   2   3       0   1   2   3    

Для полноты я заполняю нулевые значения в V и L. Теперь мы можем подняться до (1, 0) и вправо до (0, 1). Мы получаем l_10 = l_00 + r_10 = 0 + 1 = 1 и v_10 = v_00 + g_10 = 0 + 1 = 1. Справа мы получаем l_01 = l_00 + r_01 = 0 + 1 = 1 и v_01 = v_00 + g_01 = 0. Это дает нам следующую ситуацию:

          V                   L         
  +---+---+---+---+   +---+---+---+---+ 
3 |   |   |   |   | 3 |   |   |   |   | 
  +---+---+---+---+   +---+---+---+---+ 
2 |   |   |   |   | 2 |   |   |   |   | 
  +---+---+---+---+   +---+---+---+---+ 
1 | 1 |   |   |   | 1 | 1 |   |   |   | 
  +---+---+---+---+   +---+---+---+---+ 
0 | 0 | 0 |   |   | 0 | 0 | 1 |   |   | 
  +---+---+---+---+   +---+---+---+---+ 
    0   1   2   3       0   1   2   3    

Теперь мы можем подняться с (1, 0), прямо с (1, 0), что эквивалентно повышению с (0, 1), и мы можем перейти с (0, 1). Если мы поднимемся с (1, 0) до (2, 0), мы получим l_20 = l_10 + r_20 = 1 + 0 = 1 и v_20 = v_10 + g_20 = 1 + 0 = 1. Если мы пойдем прямо от (1, 0) до (1, 1), мы получим l_11 = l_10 + r_01 + r_11 = 1 + 1 + 1 = 3. Обратите внимание, что это точно так же, как повышение от (0, 1), до l_11 = l_01 + r_10 + r_11 = 1 + 1 + 1 = 3. Точно так же v_11 = v_10 + g_01 + g_11 = 1 + 1 = 2. Наконец, если мы перейдем прямо от (0, 1) до (0, 2), мы получим l_02 = l_01 + r_02 = 1 + 1 = 2 и v_02 = v_01 + g_02 = 0 + 3 = 3. Это приводит к следующей схеме:

          V                   L         
  +---+---+---+---+   +---+---+---+---+ 
3 |   |   |   |   | 3 |   |   |   |   | 
  +---+---+---+---+   +---+---+---+---+ 
2 | 1 |   |   |   | 2 | 1 |   |   |   | 
  +---+---+---+---+   +---+---+---+---+ 
1 | 1 | 2 |   |   | 1 | 1 | 3 |   |   | 
  +---+---+---+---+   +---+---+---+---+ 
0 | 0 | 0 | 3 |   | 0 | 0 | 1 | 2 |   | 
  +---+---+---+---+   +---+---+---+---+ 
    0   1   2   3       0   1   2   3    

Теперь мы можем подняться с (2, 0), прямо с (2, 0), что эквивалентно повышению с (1, 1), прямо с (1, 1), что эквивалентно повышению с (0, 2), или прямо с (0, 2). Если мы поднимемся с (2, 0) до (3, 0), мы получим l_30 = l_20 + r_30 = 1 + 2 = 3 и v_30 = v_20 + g_30 = 1 + 1 = 2.Мы можем вычислить (2, 1), поднявшись от (2, 0), но поднявшись от (1, 1), мы можем сделать то же самое с большей частью предварительно вычисленного прямоугольника (4 ячейки вместо 3). Мы получаем l_21 = l_11 + r_20 + r_21 = 3 + 0 + 1 = 4. Поскольку это превышает предел, v_21 = 0. Теперь любой прямоугольник, содержащий (2, 1), будет иметь те же самые ячейки, что и (2, 1), включая те, которые содержат до 4 красных частей. Следовательно, все прямоугольники, содержащие (2, 1), должны быть недействительными. Это все ячейки с x >= 2 и y >=1. Мы даем им l_xy = Inf для ясности. Ячейка (1, 2) содержит (0, 0), но, поскольку l_12 = l_11 + r_02 + r_12 = 3 + 1 + 0 = 4, ячейка недействительна, как и (1, 3), следуя той же логике, что и выше.

Наконец, если мы пойдем прямо от (0, 2) до (0, 3), мы получим l_03 = l_02 + r_03 = 2 + 1 = 3 и v_03 = v_02 + g_03 = 3 + 1 = 4. Это приводит к следующей схеме:

          V                   L         
  +---+---+---+---+   +---+---+---+---+ 
3 | 2 | 0 | 0 | 0 | 3 | 3 |Inf|Inf|Inf| 
  +---+---+---+---+   +---+---+---+---+ 
2 | 1 | 0 | 0 | 0 | 2 | 1 |Inf|Inf|Inf| 
  +---+---+---+---+   +---+---+---+---+ 
1 | 1 | 2 | 0 | 0 | 1 | 1 | 3 |Inf|Inf| 
  +---+---+---+---+   +---+---+---+---+ 
0 | 0 | 0 | 3 | 4 | 0 | 0 | 1 | 2 | 3 | 
  +---+---+---+---+   +---+---+---+---+ 
    0   1   2   3       0   1   2   3    

Итак, как вы можете видеть, прямоугольник с наибольшим значением - это прямоугольник, сформированный с точками (0, 3) со значением 4, и мы выяснили это при вычислении только 10 из 16 ячеек. Конечно, верхняя граница для этого алгоритма O(MN), но на практике это неизбежно, потому что рассмотрим случай, когда нет красных фигур или предел очень высок. Тогда вы все равно должны вычислить сумму всех M * N клеток.

3 голосов
/ 07 июля 2019

O (n log n) решение, где n - количество штук

Вот алгоритм, который работает путем сканирования частей, а не сетки. Я думаю, что это работает в O (p log p), где p - количество частей, а не O (grid_size ^ 2).

Это своего рода алгоритм двойной развертки. Представьте себе две линии: горизонтальную линию, определяющую верх прямоугольника (top в коде) и вертикальную линию (x), определяющую правую сторону. Верхняя линия начинается в верхней части сетки по оси Y, а вертикальная линия начинается по оси Y. Вертикальная линия смещается вправо, и когда она достигает фигуры, выполняется действие. Если кусок лежит ниже верхней строки, кусок добавляется к текущему прямоугольнику. Если будет слишком много красных кусочков, то горизонтальная линия будет смещена вниз, пока количество красных кусочков не окажется в пределах. Любые зеленые части, которые находятся на или выше горизонтальной линии, удаляются, потому что они не находятся в новом прямоугольнике. Перед тем, как переместить верхнюю часть прямоугольника вниз, проверяется количество зеленых фигур, чтобы определить, является ли оно новым максимумом.

Работает для координат с плавающей точкой.

Аналогично тому, как range() в питоне исключает верхнюю границу, прямоугольник включает (0,0), но исключает границы, возвращаемые функцией. Например, тестовый пример возвращает ((4,4), 5), означая, что прямоугольник, определенный 0 <= x <4 и 0 <= y <4, имеет 5 зеленых частей (обратите внимание на «<» на верхней границе). Для целочисленных координат прямоугольник имеет (от 0,0) до (3,3) включительно. Для координат с плавающей точкой верхняя граница исключает данную точку. </p>

import sortedcontainers as sc   # http://www.grantjenks.com/docs/sortedcontainers/

X,Y,Color = 0,1,2

def solve(pieces, size, max_reds):
    # Sort pieces by x, then red before green, then bottom-to-top
    pieces.sort(key=lambda t:(t[X],t[Color]=='g',t[Y]))

    # These keep track of the pieces that are in the rectangle. They
    # are sorted by 'y' value, so it is easy to remove pieces when 
    # 'top' is lowered due to too many reds in the rectangle.
    reds_in_rect = sc.SortedKeyList(key=lambda t:t[Y])
    greens_in_rect = sc.SortedKeyList(key=lambda t:t[Y])

    # For keeping track of the maximum number of green 
    # pieces and the corresponding coordinates.
    max_greens = 0
    max_x = 0
    max_y = 0

    # 'top' scans from top to bottom and represents the top of
    # the current rectangle.  It gets lowered to remove red pieces
    # from the rectangle when there are too many of them.   
    top = size[Y]

    # The pieces are sorted so this loop scans the pieces left-to-right.
    # If multiple pieces have the same x-coordinate, red ones come before
    # green ones, then lower ones before higher ones.
    for x,y,p in pieces:

        # Only pieces below the top of the rectangle are considered.
        # And they are added to the rectangle
        if y < top:

            if p == 'g':
                greens_in_rect.add((x,y,p))

            elif p == 'r':
                reds_in_rect.add((x,y,p))

                # If there are too many red pieces in the rectangle, 'top' gets
                # lowered to the 'y' value of the top-most red piece.  Then any
                # red or green pieces at or above the new 'top' get removed from
                # the rectangle.
                if len(reds_in_rect) > max_reds:

                    # before lowering the top of the rectangle check if current
                    # rectangle has a maximum number of green pieces
                    if len(greens_in_rect) > max_greens:
                        max_greens = len(greens_in_rect)
                        max_x = x
                        max_y = top

                    # lower to top to the 'y' value of the highest
                    # red piece seen so far
                    top = reds_in_rect[-1][Y]

                    # remove any red pieces at or above the top
                    # of the new rectangle
                    while reds_in_rect and reds_in_rect[-1][Y] >= top:
                        reds_in_rect.pop()

                    # remove any green pieces at or above the top
                    # of the new rectangle
                    while greens_in_rect and greens_in_rect[-1][Y] >= top:
                        greens_in_rect.pop()

    # last check of the number of green pieces
    if len(greens_in_rect) > max_greens:
        max_greens = len(greens_in_rect)
        max_x = size[X]
        max_y = top

    return (max_x, max_y), max_greens

Контрольный пример:

#    +-+-+-+-+-+-+
#  3 | | | |o|x|o|
#  +-+-+-+-+-+-+
#  2 |o| |x| | |o|
#    +-+-+-+-+-+-+
#  1 |o| |o| |o|x|
#    +-+-+-+-+-+-+
#  0 | |o| |x| |x|
#    +-+-+-+-+-+-+
#     0 1 2 3 4 5

size = 6,4
max_reds = 2

red = [(3,0), (5,0), (5,1), (2,2), (4,3)]
green = [(1,0), (0,1), (2,1), (4,1), (0,2), (5,2), (3,3), (5,3)]

pieces = [(x,y,'r') for x,y in red] + [(x,y,'g') for x,y in green]

solve(pieces, size, max_reds)  # --> returns ((4,5),5)
3 голосов
/ 06 июля 2019

Поскольку угол (0, 0) является обязательной частью прямоугольника, вся задача довольно проста.Алгоритм выглядит следующим образом:

при условии, что X, Y - размерность доски:

green_counts, red_counts = count_pieces()
found_pos = None
found_count = 0
for y in range(0, Y):
  x = find_x_for_y_with_max_red_pieces()
  g = green_counts(x, y)
  if g > found_count:
    found_count = g
    found_pos = x, y
print(found_pos)

Сначала мы создадим двумерный массив с красным и зеленым счетчиками для прямоугольников (0,0) ->(х, у).Затем мы итерируем по y.Для каждого y мы находим наибольшее x, для которого встречаются красные кусочки limis.Подсчитаем количество зеленых фигур и проверим, лучше ли это, чем предыдущие.Все будет работать в O(n^2), так как вам нужно рассчитать количество штук.

Обратите внимание, что вы можете улучшить требования к памяти еще больше (вам не нужен полный двумерный массив, вам нужно только "предыдущий «ряд».

Вопрос 2: что делать с плавающими позициями?Такой же.Отсортируйте x позиций и замените их индексом.Так, например, для точек (0, 0), (0.2, 1), (0.2, 2.5), (0.4, 1) вы получите (0, 0), (1, 1), (1, 2), (2, 1).Затем используйте алгоритм выше.

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