Je me suis entraîné pour un prochain concours de programmation et je suis tombé sur une question qui me déroute complètement. Cependant, j'ai l'impression que c'est un concept que je devrais apprendre maintenant plutôt que de croiser les doigts pour qu'il ne revienne jamais.
Fondamentalement, il s'agit d'une pièce de chevalier sur un échiquier. On vous donne deux entrées: le lieu de départ et le lieu de fin. Le but est ensuite de calculer et d'imprimer le chemin le plus court que le chevalier puisse emprunter pour se rendre à l'emplacement cible.
Je n'ai jamais traité des choses les plus courtes, et je ne sais même pas par où commencer. Quelle logique dois-je utiliser pour aborder ce problème?
PS Si cela est pertinent, ils veulent que vous complétiez les mouvements normaux du chevalier en lui permettant également de se déplacer aux quatre coins du carré formé par les huit (potentiellement) mouvements qu'un chevalier peut faire, étant donné que le centre du carré est l'emplacement du chevalier.
la source
Réponses:
Vous avez un graphique ici, où tous les mouvements disponibles sont connectés (valeur = 1), et les mouvements non disponibles sont déconnectés (valeur = 0), la matrice éparse serait comme:
Et le chemin le plus court de deux points dans un graphique peut être trouvé en utilisant http://en.wikipedia.org/wiki/Dijkstra's_algorithm
Pseudo-code de la page wikipedia:
ÉDITER:
Introduction to Algorithms
ISBN 0-262-03384-4. Ou vous pouvez essayer wikipedia, http://en.wikipedia.org/wiki/List_of_algorithmsla source
EDIT: Voir la réponse de Simon , où il a fixé la formule présentée ici.
En fait, il existe une formule O (1)
C'est une image que j'ai faite pour la visualiser (les carrés qu'un chevalier peut atteindre sur N th coup sont peintes avec la même couleur).
Pouvez-vous remarquer le modèle ici?
Bien que nous puissions voir le modèle, il est vraiment difficile de trouver la fonction
f( x , y )
qui renvoie le nombre de mouvements nécessaires pour passer du carré( 0 , 0 )
en carré( x , y )
Mais voici la formule qui fonctionne quand
0 <= y <= x
Remarque: Cette question a été posée lors du SACO 2007 Day 1
et les solutions sont ici
la source
2 * floor((y - delta) / 3) + delta
etdelta - 2 * floor((delta - y) / 4)
. C'est la solution officielle de cette page de concours, mais c'est faux. Cette première équation (deif
) renvoie de mauvaises réponses. Sur l'échiquier [-1000..1000] x [-1000..1000], qui est grand 2001x2001 (mais logiquement illimité), la réponse donnée compte 2 669 329 sur 4 004 001 champs corrects (66,66%). Quelqu'un connaît la solution de travail sans aucune boucle?Voici une solution O (1) correcte, mais pour le cas où le chevalier se déplace comme un chevalier d'échecs uniquement, et sur un échiquier infini:
https://jsfiddle.net/graemian/5qgvr1ba/11/
La clé pour trouver cela est de remarquer les motifs qui émergent lorsque vous dessinez le tableau. Dans le diagramme ci-dessous, le nombre dans le carré est le nombre minimum de mouvements nécessaires pour atteindre ce carré (vous pouvez utiliser la recherche en largeur d'abord pour le trouver):
Parce que la solution est symétrique sur les axes et les diagonales, je n'ai dessiné que le cas x> = 0 et y> = x.
Le bloc en bas à gauche est la position de départ et les nombres dans les blocs représentent le nombre minimum de mouvements pour atteindre ces blocs.
Il y a 3 modèles à remarquer:
(Assurez-vous de voir les deux ensembles de diagonales en haut à gauche en bas à droite. Ils ont un nombre de mouvements constant. Les diagonales en bas à gauche et en haut à droite sont beaucoup plus complexes.)
Vous pouvez dériver des formules pour chacun. Les blocs jaunes sont des cas particuliers. La solution devient donc:
le plus difficile étant les groupes verticaux:
Voir le violon pour les autres cas.
Peut-être y a-t-il des motifs plus simples ou plus élégants que j'ai manqués? Si oui, j'aimerais les voir. En particulier, je remarque des motifs diagonaux dans les cas verticaux bleus, mais je ne les ai pas explorés. Quoi qu'il en soit, cette solution satisfait toujours la contrainte O (1).
la source
Problème très intéressant que j'ai rencontré récemment. Après avoir cherché des solutions, j'ai essayé de récupérer la formule analytique (
O(1) time and space complexity
) donnée sur les solutions SACO 2007 Day 1 .Tout d'abord je veux apprécier Graeme Pyle pour sa très belle visualisation qui m'a aidé à corriger la formule.
Pour une raison quelconque (peut-être par simplification, beauté ou simplement une erreur), ils ont déplacé le
minus
signe dans l'floor
opérateur, en conséquence ils ont une mauvaise formulefloor(-a) != -floor(a) for any a
.Voici la formule analytique correcte:
La formule fonctionne pour toutes les paires (x, y) (après application des axes et de la symétrie diagonale), à l'exception des cas d'angle (1,0) et (2,2), qui ne sont pas satisfaits au motif et codés en dur dans l'extrait suivant:
Remarque: le jQuery utilisé à titre d'illustration uniquement, pour le code, voir la
distance
fonction.la source
Oui, Dijkstra et BFS vous apporteront la réponse, mais je pense que le contexte d'échecs de ce problème fournit des connaissances qui peuvent fournir une solution beaucoup plus rapide qu'un algorithme générique à plus court chemin, en particulier sur un échiquier infini.
Pour simplifier, décrivons l'échiquier comme le plan (x, y). Le but est de trouver le chemin le plus court de (x0, y0) à (x1, y1) en utilisant uniquement les étapes candidates (+ -1, + -2), (+ -2, + -1) et (+ -2 , + -2), comme décrit dans le PS de la question
Voici la nouvelle observation: dessinez un carré avec des coins (x-4, y-4), (x-4, y + 4), (x + 4, y-4), (x + 4, y + 4) . Cet ensemble (appelez-le S4) contient 32 points. Le chemin le plus court de l'un de ces 32 points à (x, y) nécessite exactement deux mouvements .
Le chemin le plus court de l'un des 24 points de l'ensemble S3 (défini de manière similaire) à (x, y) nécessite au moins deux mouvements .
Par conséquent, si | x1-x0 |> 4 ou | y1-y0 |> 4, le chemin le plus court de (x0, y0) à (x1, y1) est exactement deux mouvements plus grand que le chemin le plus court de (x0, y0) à S4. Et ce dernier problème peut être résolu rapidement avec une itération simple.
Soit N = max (| x1-x0 |, | y1-y0 |). Si N> = 4, alors le chemin le plus court de (x0, y0) à (x1, y1) a ceil (N / 2) pas.
la source
La réponse O (1) ci-dessus [ https://stackoverflow.com/a/8778592/4288232 par Mustafa Serdar Şanlı] ne fonctionne pas vraiment. (Vérifiez (1,1) ou (3,2) ou (4,4), mis à part les cas de bord évidents de (1,0) ou (2,2)).
Voici une solution beaucoup plus moche (python), qui fonctionne (avec des "tests" ajoutés):
la source
above
oubelow
ne fonctionne pas vraiment sur SO.Ce que vous devez faire est de penser aux mouvements possibles du chevalier comme un graphique, où chaque position sur le plateau est un nœud et les mouvements possibles vers une autre position comme un bord. Il n'est pas nécessaire d'utiliser l'algorithme de dijkstra, car chaque arête a le même poids ou la même distance (elles sont toutes aussi faciles ou courtes à faire). Vous pouvez simplement effectuer une recherche BFS à partir de votre point de départ jusqu'à ce que vous atteigniez la position finale.
la source
Solution des premiers principes en Python
J'ai rencontré ce problème pour la première fois lors d'un test Codility. Ils m'ont donné 30 minutes pour le résoudre - il m'a fallu beaucoup plus de temps pour arriver à ce résultat! Le problème était le suivant: combien de mouvements faut-il à un chevalier pour passer de 0,0 à x, y en utilisant uniquement les mouvements légaux du chevalier. x et y étaient plus ou moins illimités (nous ne parlons donc pas ici d'un simple échiquier 8x8).
Ils voulaient une solution O (1). Je voulais une solution où le programme résolvait clairement le problème (c'est-à-dire que je voulais quelque chose de plus manifestement juste que le modèle de Graeme - les modèles ont l'habitude de s'effondrer là où vous ne regardez pas), et je voulais vraiment ne pas avoir à compter sur un formule non argumentée, comme dans la solution de Mustafa
Alors, voici ma solution, pour ce que ça vaut. Commencez, comme d'autres l'ont fait, en notant que la solution est symétrique autour des axes et des diagonales, nous devons donc résoudre uniquement pour 0> = y> = x. Pour simplifier l'explication (et le code), je vais inverser le problème: le chevalier commence à x, y et vise 0,0.
Supposons que nous réduisions le problème au voisinage de l'origine. Nous verrons ce que signifie réellement `` vicinty '' en temps voulu, mais pour l'instant, écrivons simplement quelques solutions dans une feuille de triche (origine en bas à gauche):
Donc, étant donné x, y sur la grille, nous pouvons simplement lire le nombre de mouvements vers l'origine.
Si nous avons commencé en dehors du réseau, nous devons y revenir. Nous introduisons la «ligne médiane», qui est la ligne représentée par y = x / 2. Tout chevalier à x, y sur cette ligne peut revenir à la feuille de triche en utilisant une série de mouvements à 8 heures (c'est-à-dire: (-2, -1) mouvements). Si x, y se trouve au-dessus de la ligne médiane, alors nous aurons besoin d'une succession de mouvements de 8 heures et 7 heures, et s'il se trouve en dessous de la ligne médiane, nous aurons besoin d'une succession de 8 heures et 10 heures. «l'horloge bouge. Deux choses à noter ici:
Alors, regardons les mouvements au-dessus de la ligne médiane. Ce que nous affirmons, c'est que:
(dx; dy) = (2,1; 1,2) (n8; n7) (notation matricielle, sans composition mathématique - le vecteur colonne (dx; dy) est égal à la matrice carrée multipliée par le vecteur colonne (n8; n7) - le nombre de mouvements à 8 heures et nombre de mouvements à 7 heures), et de même;
(dx; dy) = (2,2; 1, -1) (n8; n10)
Je prétends que dx, dy sera à peu près (x, y), donc (x-dx, y-dy) sera à proximité de l'origine (quel que soit le «voisinage»).
Les deux lignes du code qui calculent ces termes sont la solution à ceux-ci, mais elles sont sélectionnées pour avoir des propriétés utiles:
(Voulez-vous des preuves de cela?) Ainsi, la distance du chevalier sera la somme de n7, n8, n10 et de la feuille de triche [x-dx, y-dy], et notre feuille de triche se réduit à ceci:
Maintenant, ce n'est pas tout à fait la fin de l'histoire. Regardez le 3 sur la rangée du bas. Les seuls moyens d'y parvenir sont soit:
Il y a une optimisation similaire à avoir avec le 4 en haut à droite. En dehors de partir de là, le seul moyen d'y parvenir est de passer à 8 heures de (4,3). Ce n'est pas sur la feuille de triche, mais s'il y était, sa distance serait de 3, car nous pourrions avoir 7 heures à (3,1) à la place, qui a une distance de seulement 2. Donc, nous devrions revenir en arrière Mouvement de 8 heures, puis avance de 7 heures.
Nous devons donc ajouter un numéro de plus à la feuille de triche:
(Remarque: il y a tout un tas d'optimisations de back-tracking de (0,1) et (0,2) mais comme le solveur ne nous y mènera jamais, nous n'avons pas à nous en soucier.)
Alors, voici un code Python pour évaluer ceci:
Soit dit en passant, si vous voulez connaître un itinéraire réel, alors cet algorithme le fournit également: il s'agit simplement d'une succession de n7 coups à 7 heures, suivis de (ou entrecoupés de) n8 coups à 8 heures, n10 10- l'heure se déplace, et quelle que soit la danse dictée par le cheatsheet (qui, lui-même, peut être dans un cheatsheet).
Maintenant: comment prouver que c'est vrai. Il ne suffit pas de comparer ces résultats avec un tableau de bonnes réponses, car le problème lui-même est illimité. Mais nous pouvons dire que, si la distance du chevalier d'un carré s est d, alors si {m} est l'ensemble des mouvements légaux de s, la distance du chevalier de (s + m) doit être soit d-1 ou d + 1 pour tous m. (Avez-vous besoin d'une preuve de cela?) De plus, il doit y avoir au moins un de ces carrés dont la distance est d-1, sauf si s est l'origine. Ainsi, nous pouvons prouver l'exactitude en montrant que cette propriété est valable pour chaque carré. Donc:
Alternativement, nous pouvons prouver l'exactitude de n'importe quel carré s en poursuivant l'itinéraire de la descente à l'origine. Tout d'abord, vérifiez le caractère raisonnable de s comme ci-dessus, puis sélectionnez tout s + m tel que distance (s + m) == d-1. Répétez jusqu'à ce que nous atteignions l'origine.
Howzat?
la source
Je n'ai pas encore étudié les graphes ... comme pour le problème de sa mise en œuvre par le biais de simples tableaux, je ne pourrais pas en tirer une autre solution. J'ai traité les positions non pas comme des rangs et des fichiers (la notation habituelle des échecs), mais comme des indices de tableau. FYI, ceci est pour un échiquier 8 * 8 seulement. Tout conseil d'amélioration est toujours le bienvenu.
* Les commentaires devraient suffire à votre compréhension de la logique. Cependant, vous pouvez toujours demander.
* Vérifié sur le compilateur DEV-C ++ 4.9.9.2 (Bloodshed Software).
la source
Je pense que cela pourrait aussi vous aider.
et en utilisant la programmation dynamique pour obtenir la solution.
PS: Il utilise un peu le BFS sans avoir à se donner la peine de déclarer les nœuds et les arêtes du graphe.
la source
Voici une solution à ce problème particulier implémenté en Perl. Il montrera l'un des chemins les plus courts - il peut y en avoir plus d'un dans certains cas.
Je n'ai utilisé aucun des algorithmes décrits ci-dessus - mais ce serait bien de le comparer à d'autres solutions.
la source
la source
Juste le code ruby du jsfiddle de la réponse de Graeme Pyle ci - dessus , rayé tout le code supplémentaire et converti en ruby juste pour obtenir une solution par son algorithme, semble fonctionner. Toujours en test cependant:
La seule intention est de faire gagner du temps à quelqu'un pour convertir le code si quelqu'un a besoin du code complet.
la source
voici la version PHP de la fonction de Jules May
la source
Voici mon programme. Ce n’est pas une solution parfaite. Il y a beaucoup de changements à apporter à la fonction de récursivité. Mais ce résultat final est parfait. J'ai essayé d'optimiser un peu.
la source
Voici une version C basée sur le code Mustafa Serdar Şanlı qui fonctionne pour une carte finit:
Testez-le ici avec une preuve contre une solution récursive
la source