Imagine una cuadrícula de cuadrados W por H que se envuelve toroidalmente. Los elementos se colocan en la cuadrícula de la siguiente manera.
El primer elemento se puede colocar en cualquier casilla, pero los elementos posteriores no deben estar dentro de una distancia R de Manhattan de ningún elemento anterior (también conocido como vecindario Von Neumann de rango R ). Elegir cuidadosamente las posiciones permite ajustar una gran cantidad de elementos en la cuadrícula antes de que no haya más posiciones válidas. Sin embargo, considere el objetivo opuesto: ¿Cuál es el menor número de elementos que se pueden colocar y no dejan más posiciones válidas?
Aquí hay una zona de exclusión de radio 5:
Aquí hay otra zona de exclusión de radio 5, esta vez cerca de los bordes para que el comportamiento de ajuste sea evidente:
Entrada
Tres enteros:
- W : ancho de la cuadrícula (entero positivo)
- H : altura de la cuadrícula (entero positivo)
- R : radio de zona de exclusión (entero no negativo)
Salida
Un entero N , que es el número más pequeño de elementos que se pueden colocar, lo que evita cualquier ubicación válida adicional.
Detalles
- Un radio de cero da una zona de exclusión de 1 cuadrado (en el que se colocó el elemento).
- Un radio de N excluye la zona que se puede alcanzar en N pasos ortogonales (recuerde que los bordes se envuelven toroidalmente).
Su código debe funcionar para el caso trivial de R = 0, pero no necesita funcionar para W = 0 o H = 0.
El código también debe lidiar con el caso en que R > W o R > H .
Límite de tiempo y casos de prueba
Su código debe poder manejar todos los casos de prueba, y cada caso de prueba debe completarse en 5 minutos. Esto debería ser fácil (la solución de JavaScript de ejemplo tarda unos segundos en cada caso de prueba). El límite de tiempo es principalmente para excluir el enfoque de fuerza bruta extrema. El enfoque de ejemplo sigue siendo la fuerza bruta.
Si su código se completa en 5 minutos en una máquina pero no en otra, estará lo suficientemente cerca.
Casos de prueba en las entradas de formulario: salida comoW 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
Fragmento para ayudar a visualizar y jugar con ideas
canvas = document.getElementById('gridCanvas');ctx = canvas.getContext('2d');widthSelector = document.getElementById('width');heightSelector = document.getElementById('height');radiusSelector = document.getElementById('radius');itemCount = document.getElementById('itemCount');canvasMaxWidth = canvas.width;canvasMaxHeight = canvas.height;gridlineColour = '#888';emptyColour = '#ddd';itemColour = '#420';hoverCellColour = '#840';exclusionColour = '#d22';validColour = '#8f7';invalidColour = '#fbb';overlapColour = '#600';resetGrid();x = -1;y = -1;function resetGrid() {gridWidth = widthSelector.value;gridHeight = heightSelector.value;radius = radiusSelector.value;numberOfItems = 0;itemCount.value = numberOfItems + ' items placed.';cells = [gridWidth * gridHeight];for (i=0; i<gridWidth*gridHeight; i++) {cells[i] = '#ddd';}cellSize = Math.min(Math.floor(canvasMaxWidth/gridWidth), Math.floor(canvasMaxHeight/gridHeight));pixelWidth = gridWidth * cellSize + 1;canvas.width = pixelWidth;pixelHeight = gridHeight * cellSize + 1;canvas.height = pixelHeight;leaveCanvas();}function checkPosition(event) {bound = canvas.getBoundingClientRect();canvasX = event.clientX - bound.left;canvasY = event.clientY - bound.top;newX = Math.min(Math.floor(canvasX / cellSize), gridWidth-1);newY = Math.min(Math.floor(canvasY / cellSize), gridHeight-1);if (newX != x || newY != y) {x = newX;y = newY;drawGrid();}}function leaveCanvas() {x = -1;y = -1;drawGrid();}function drawGrid() {ctx.fillStyle = gridlineColour;ctx.fillRect(0, 0, pixelWidth, pixelHeight);cellColour = cells[x + gridWidth * y];if (cellColour == itemColour || cellColour == exclusionColour) {zoneColour = invalidColour;} else {zoneColour = validColour;}for (gridX=0; gridX<gridWidth; gridX++) {for (gridY=0; gridY<gridHeight; gridY++) {colour = (cells[gridX + gridWidth * gridY]);if (x >= 0 && y >= 0) {if (x == gridX && y == gridY) {colour = hoverCellColour;} else if (manhattanDistance(x, y, gridX, gridY) <= radius) {if (colour == exclusionColour) {colour = overlapColour;} else if (colour != itemColour) {colour = zoneColour;}}}ctx.fillStyle = colour;ctx.fillRect(gridX * cellSize + 1, gridY * cellSize + 1, cellSize - 1, cellSize - 1);}}}function manhattanDistance(a, b, c, d) {horizontal = Math.min(Math.abs((gridWidth + c - a) % gridWidth), Math.abs((gridWidth + a - c) % gridWidth));vertical = Math.min(Math.abs((gridHeight + d - b) % gridHeight), Math.abs((gridHeight + b - d) % gridHeight));return vertical + horizontal;}function placeItem(event) {bound = canvas.getBoundingClientRect();canvasX = event.clientX - bound.left;canvasY = event.clientY - bound.top;attemptX = Math.min(Math.floor(canvasX / cellSize), gridWidth-1);attemptY = Math.min(Math.floor(canvasY / cellSize), gridHeight-1);colour = cells[attemptX + gridWidth * attemptY];if (colour != itemColour && colour != exclusionColour) {for (a=0; a<gridWidth; a++) {for (b=0; b<gridHeight; b++) {if (manhattanDistance(a, b, attemptX, attemptY) <= radius) {cells[a + gridWidth * b] = exclusionColour;}}}cells[attemptX + gridWidth * attemptY] = itemColour;drawGrid();numberOfItems++;itemCount.value = numberOfItems + ' items placed.';}}
<canvas id='gridCanvas' width='500' height='300' style='border:none' onmousemove='checkPosition(event)' onmouseleave='leaveCanvas()' onclick='placeItem(event)'></canvas><br><textarea id='itemCount' readonly></textarea><br><button id='reset' onclick='resetGrid()'>Reset</button> with the following values:<br>Width:<input id='width' type='number' value='20' min='1' max='50' maxlength='2' step='1'><br>Height:<input id='height' type='number' value='13' min='1' max='30' maxlength='2' step='1'><br>Radius:<input id='radius' type='number' value='5' min='0' max='60' maxlength='2' step='1'>
Solución de ejemplo (sin golf)
Solo un ejemplo para salidas pequeñas (resultantes de un radio no mucho más pequeño que el ancho y la altura). Puede manejar cualquiera de los casos de prueba, pero expirará y se dará por vencido en la mayoría de los casos más grandes.
itemCount = document.getElementById('itemCount')
calculateButton = document.getElementById('calculate')
widthSelector = document.getElementById('width')
heightSelector = document.getElementById('height')
radiusSelector = document.getElementById('radius')
function calculate() {
calculateButton.disabled = true
widthSelector.disabled = true
heightSelector.disabled = true
radiusSelector.disabled = true
itemCount.value = 'Calculating...'
setTimeout(delayedCalculate, 100)
}
function delayedCalculate() {
gridWidth = widthSelector.value
gridHeight = heightSelector.value
radius = radiusSelector.value
startingMin = gridWidth*gridHeight + 1
var cells = [gridWidth * gridHeight]
for (i=0; i<gridWidth*gridHeight; i++) {
cells[i] = 0
}
var gridState = gridWithItemAdded(cells, 0, 0)
var minimum = minFromHere(gridState) + 1
itemCount.value = 'Minimum ' + minimum + ' items required.'
calculateButton.disabled = false
widthSelector.disabled = false
heightSelector.disabled = false
radiusSelector.disabled = false
}
function minFromHere(gridState) {
var x
var y
var newGridState
var noCells = true
var min = startingMin
for (x=0; x<gridWidth; x++) {
for (y=0; y<gridHeight; y++) {
if (gridState[x + gridWidth * y] == 0) {
noCells = false
newGridState = gridWithItemAdded(gridState, x, y)
m = minFromHere(newGridState)
if (m < min) {
min = m
}
}
}
}
if (noCells) return 0
return min + 1
}
function gridWithItemAdded(gridState, x, y) {
var a
var b
var grid = gridState.slice()
for (a=0; a<gridWidth; a++) {
for (b=0; b<gridHeight; b++) {
if (manhattanDistance(a, b, x, y) <= radius) {
grid[a + gridWidth * b] = 1
}
}
}
return grid
}
function manhattanDistance(a, b, c, d) {
horizontal = Math.min(Math.abs((gridWidth + c - a) % gridWidth), Math.abs((gridWidth + a - c) % gridWidth))
vertical = Math.min(Math.abs((gridHeight + d - b) % gridHeight), Math.abs((gridHeight + b - d) % gridHeight))
return vertical + horizontal
}
<textarea id='itemCount' readonly></textarea>
<br>
<button id='calculate' onclick='calculate()'>Calculate</button> with the following values:
<br>
Width:<input id='width' type='number' value='5' min='1' max='50' maxlength='2' step='1'>
<br>
Height:<input id='height' type='number' value='4' min='1' max='30' maxlength='2' step='1'>
<br>
Radius:<input id='radius' type='number' value='1' min='0' max='60' maxlength='2' step='1'>
Respuestas:
Python 2,
216182 bytesEntrada como
f(16,16,8)
. Utiliza prácticamente el mismo algoritmo que la muestra de @ trichoplax , pero con conjuntos. Inicialmente no coloqué el primer elemento(0, 0)
, pero esto hizo que se ahogara en los últimos casos.Todos los casos anteriores se completan en 10 segundos, muy por debajo del límite. De hecho, los casos son lo suficientemente pequeños como para tener un poco de espacio para ser menos eficiente, lo que me permite eliminar un cheque que verificó los estados duplicados.
(Gracias a @trichoplax por la ayuda en el golf)
Expandido:
fuente
Python 3,
270262260251246226(Gracias a Sp3000 por:
-~
en lugar de+1
, lo que me permite perder un espacio despuésreturn
en la última línea.W*H
.%
da resultados positivos para números negativos, para guardar otros 20 bytes)Esta es la respuesta de ejemplo de JavaScript de la pregunta portada a Python 3.
Para evitar tener que pasar tantos argumentos de función, he movido las dos funciones de soporte dentro de la función de cálculo para que compartan su alcance. También he condensado cada una de estas funciones en una sola línea para evitar el costo de la sangría.
Explicación
Este enfoque de fuerza bruta coloca el primer elemento en (0, 0) y luego marca todos los cuadrados excluidos. Luego coloca recursivamente un elemento en todos los cuadrados válidos restantes hasta que se excluyan todos los cuadrados, y devuelve el número mínimo de elementos requeridos.
Código de golf:
Código sin golf:
El código no definido define una función y también incluye código para permitir que se llame desde la línea de comandos. El código de golf solo define la función, que es suficiente para las preguntas de golf de código estándar .
Si desea probar el código de golf desde la línea de comando, aquí está con el manejo de línea de comando incluido (pero no golfizado):
Código de golf comprobable de línea de comando
fuente