Imaginez une grille de carrés W par H qui s'enroule toroïdalement. Les éléments sont placés sur la grille comme suit.
Le premier élément peut être placé sur n'importe quelle case, mais les éléments suivants ne doivent pas être à une distance Manhattan R de tout élément précédent (également connu sous le nom de quartier Von Neumann de la gamme R ). Le choix judicieux des positions permet de placer un grand nombre d'articles sur la grille avant qu'il n'y ait plus de positions valides. Cependant, considérez plutôt l'objectif inverse: quel est le plus petit nombre d'articles pouvant être placés et ne laisser aucune autre position valide?
Voici une zone d'exclusion de rayon 5:
Voici une autre zone d'exclusion de rayon 5, cette fois près des bords, donc le comportement d'enveloppement est apparent:
Contribution
Trois entiers:
- W : largeur de la grille (entier positif)
- H : hauteur de la grille (entier positif)
- R : rayon de la zone d'exclusion (entier non négatif)
Production
Un entier N , qui est le plus petit nombre d'éléments pouvant être placés, empêchant tout autre placement valide.
Détails
- Un rayon de zéro donne une zone d'exclusion de 1 carré (celle sur laquelle l'objet a été placé).
- Un rayon de N exclut la zone qui peut être atteinte en N pas orthogonaux (rappelez-vous que les bords s'enroulent toroïdalement).
Votre code doit fonctionner pour le cas trivial de R = 0, mais n'a pas besoin de fonctionner pour W = 0 ou H = 0.
Votre code doit aussi faire face au cas où R > W ou R > H .
Délai et cas de test
Votre code doit être capable de gérer tous les cas de test, et chaque cas de test doit se terminer dans les 5 minutes. Cela devrait être facile (l'exemple de solution JavaScript prend quelques secondes pour chaque cas de test). La limite de temps est principalement d'exclure l'approche extrême de la force brute. L'exemple d'approche est encore assez brutal.
Si votre code se termine dans les 5 minutes sur une machine mais pas sur une autre, ce sera assez proche.
Cas de test sous la forme entrées: sortie en tant queW 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
Extrait pour aider à visualiser et à jouer avec les idées
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'>
Exemple de solution (non golfée)
Juste un exemple pour les petites sorties (résultant d'un rayon pas beaucoup plus petit que la largeur et la hauteur). Peut gérer n'importe lequel des cas de test, mais expirera et abandonnera pour la plupart des cas plus grands.
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'>
Réponses:
Python 2,
216182 octetsEntrez comme
f(16,16,8)
. Utilise à peu près le même algorithme que l'échantillon de @ trichoplax , mais avec des ensembles. Au début, je n'ai pas placé le premier élément sur(0, 0)
, mais cela l'a étranglé dans les derniers cas.Tous les cas ci-dessus se terminent en 10 secondes, bien en dessous de la limite. En fait, les boîtiers sont suffisamment petits pour que j'avais un peu de place pour être moins efficace, ce qui me permet de supprimer un chèque qui vérifiait les états en double.
(Merci à @trichoplax pour son aide au golf)
Étendu:
la source
Python 3,
270262260251246226(Merci à Sp3000 pour:
-~
au lieu de+1
, ce qui me permet de perdre un espace aprèsreturn
sur la dernière ligne.W*H
.%
donne un résultat positif pour les nombres négatifs, pour économiser encore 20 octets)Il s'agit de l'exemple de réponse JavaScript de la question portée dans Python 3.
Pour éviter d'avoir à passer autant d'arguments de fonction, j'ai déplacé les deux fonctions de support à l'intérieur de la fonction de calcul afin qu'elles partagent sa portée. J'ai également condensé chacune de ces fonctions en une seule ligne pour éviter le coût de l'indentation.
Explication
Cette approche assez brutale place le premier élément à (0, 0), puis marque tous les carrés exclus. Il place ensuite récursivement un élément sur tous les carrés valides restants jusqu'à ce que tous les carrés soient exclus et renvoie le nombre minimum d'éléments requis.
Code golf:
Code non golfé:
Le code non golfé définit une fonction et inclut également du code pour lui permettre d'être appelé à partir de la ligne de commande. Le code golfé définit simplement la fonction, ce qui est suffisant pour les questions de golf de code standard .
Si vous souhaitez tester le code golfé à partir de la ligne de commande, le voici avec la gestion de la ligne de commande incluse (mais pas golfée):
Code golfable testable en ligne de commande
la source