Наихудший случай исключения из Манхэттена

20

Представьте сетку квадратов W на H, которая оборачивается тороидально. Элементы размещаются на сетке следующим образом.

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

Вот зона отчуждения радиуса 5:

Радиус 5 зоны отчуждения

Вот еще одна зона исключения радиуса 5, на этот раз около краев, поэтому поведение оборачивания очевидно:

Радиус обмотки 5 зоны отчуждения

вход

Три целых числа:

  • W : ширина сетки (положительное целое число)
  • H : высота сетки (положительное целое число)
  • R : радиус зоны исключения (неотрицательное целое число)

Выход

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

Детали

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

Ваш код должен работать для тривиального случая R = 0, но не должен работать для W = 0 или H = 0.

Ваш код должен также иметь дело со случаем , когда R > W или R > H .

Ограничение по времени и контрольные примеры

Ваш код должен быть в состоянии справиться со всеми тестовыми примерами, и каждый тестовый случай должен быть выполнен в течение 5 минут. Это должно быть легко (пример решения JavaScript занимает несколько секунд для каждого теста). Ограничение по времени главным образом исключает использование подхода грубой силы. Примерный подход все еще довольно грубая сила.

Если ваш код завершается в течение 5 минут на одной машине, но не на другой, это будет достаточно близко.

Тестовые случаи в форме ввода: вывод какW H R : N

5 4 4 : 1
5 4 3 : 2
5 4 2 : 2
5 4 1 : 5

7 5 5 : 1
7 5 4 : 2
7 5 3 : 2
7 5 2 : 4

8 8 8 : 1
8 8 7 : 2
8 8 6 : 2
8 8 5 : 2
8 8 4 : 2
8 8 3 : 4

 7  6  4 : 2
 7  6  2 : 4
11  7  4 : 3
11  9  4 : 4
13 13  6 : 3
11 11  5 : 3
15 14  7 : 2
16 16  8 : 2

Фрагмент, чтобы помочь визуализировать и поиграть с идеями

Пример (безгольфовое) решение

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

Trichoplax
источник
4
Отличный фрагмент кода!
Растянуть маньяк
@StretchManiac спасибо :) Я пытаюсь выучить JavaScript, поэтому любые отзывы приветствуются
trichoplax
1
Это довольно хороший фрагмент. Мне тоже нравится эта цветовая схема. Это из палитры?
миль
@ Майлс спасибо - цвета только угаданы, а затем немного настроены (но не очень - они все еще являются 3-цветными кодами символов, а не 6). Вы можете увидеть цвета, используемые в третьем блоке строк в коде фрагмента.
Трихоплакс

Ответы:

5

Python 2, 216 182 байта

def f(W,H,R):L={(i%W,i/W)for i in range(W*H)};M={(x,y)for x,y in L if min(x,W-x)+min(y,H-y)>R};g=lambda s:min([1+g(s-{((a+x)%W,(b+y)%H)for x,y in L-M})for a,b in s]or[1]);return g(M)

Ввод как f(16,16,8). Использует тот же алгоритм, что и в примере @ trichoplax , но с множествами. Первоначально я не помещал первый элемент в (0, 0), но это сделало его удушающим в последних нескольких случаях.

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

(Спасибо @trichoplax за помощь в игре в гольф)

Expanded:

def f(W,H,R):
  # All cells
  L={(i%W,i/W)for i in range(W*H)}                 

  # Mask: Complement of exclusion zone around (0, 0) 
  M={(x,y)for x,y in L if min(x,W-x)+min(y,H-y)>R}

  # Place recursively
  g=lambda s:min([1+g(s-{((a+x)%W,(b+y)%H)for x,y in L-M})for a,b in s]or[1])
  return g(M)
Sp3000
источник
2

Питон 3, 270 262 260 251 246 226

(Спасибо Sp3000 за:

  • -~ вместо того +1 , что позволяет мне потерять пробел после return последней строки.
  • теряя лишние скобки вокруг W*H .
  • лямбды ...
  • положить все в одну строку.
  • Python по модулю %дает положительный результат для отрицательных чисел, чтобы сохранить еще 20 байтов)

Это пример ответа JavaScript из вопроса, перенесенного в Python 3.

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

объяснение

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

Гольф-код:

def C(W,H,R):r=range;M=lambda g:min([M(G(g,x,y))for x in r(W)for y in r(H)if g[x+W*y]]or[-1])+1;G=lambda g,x,y:[g[a+W*b]if min((x-a)%W,(a-x)%W)+min((y-b)%H,(b-y)%H)>R else 0for b in r(H)for a in r(W)];return-~M(G([1]*W*H,0,0))

Ungolfed код:

def calculate(W, H, R):
    starting_min = W * H + 1
    cells = [0] * (W * H)
    grid_state = grid_with_item_added(cells, 0, 0, W, H, R)
    return min_from_here(grid_state, starting_min, W, H, R) + 1

def min_from_here(grid_state, starting_min, W, H, R):
    no_cells = True
    min = starting_min
    for x in range(W):
        for y in range(H):
            if grid_state[x + W * y] == 0:
                no_cells = False
                new_grid_state = grid_with_item_added(grid_state, x, y, W, H, R)
                m = min_from_here(new_grid_state, starting_min, W, H, R)
                if m < min:
                    min = m

    if no_cells:
        return 0
    else:
        return min + 1

def grid_with_item_added(grid_state, x, y, W, H, R):
    grid = grid_state[:]
    for a in range(W):
        for b in range(H):
            if manhattan_distance(a, b, x, y, W, H) <= R:
                grid[a + W * b] = 1

    return grid

def manhattan_distance(a, b, c, d, W, H):
    horizontal = min(abs(W + c - a) % W, abs(W + a - c) % W)
    vertical = min(abs(H + d - b) % H, abs(H + b - d) % H)
    return horizontal + vertical


if __name__ == '__main__':
    import sys
    arguments = sys.argv[1:]
    if len(arguments) < 3:
        print('3 arguments required: width, height and radius')
    else:
        print(calculate(int(arguments[0]), int(arguments[1]), int(arguments[2])))

Код ungolfed определяет функцию, а также содержит код, позволяющий вызывать ее из командной строки. Код для игры в гольф просто определяет функцию, которая достаточна для стандартных вопросов по гольфу .

Если вы хотите протестировать гольф-код из командной строки, то здесь с включенной обработкой командной строки (но не для игры в гольф):

Командная строка проверяемый гольф-код

def C(W,H,R):r=range;M=lambda g:min([M(G(g,x,y))for x in r(W)for y in r(H)if g[x+W*y]]or[-1])+1;G=lambda g,x,y:[g[a+W*b]if min((x-a)%W,(a-x)%W)+min((y-b)%H,(b-y)%H)>R else 0for b in r(H)for a in r(W)];return-~M(G([1]*W*H,0,0))

if __name__ == '__main__':
    import sys
    arguments = sys.argv[1:]
    if len(arguments) < 3:
        print('3 arguments required: width, height and radius')
    else:
        print(C(int(arguments[0]), int(arguments[1]), int(arguments[2])))
Trichoplax
источник