Chemin le plus court du chevalier sur l'échiquier


95

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.


Pourriez-vous clarifier le PS? Vous voulez dire, si un chevalier est sur E4, il peut passer à C2, C6, G2 et G6?
Steve Tjoa

Oui, en plus de ses mouvements normaux.
Kyle Hughes

1
Voici une analyse mathématique du problème: math.stackexchange.com/questions/104700/…
Graeme Pyle

Réponses:


28

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:

(a1,b3)=1,
(a1,c2)=1,
  .....

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:

function Dijkstra(Graph, source):
   for each vertex v in Graph:           // Initializations
       dist[v] := infinity               // Unknown distance function from source to v
       previous[v] := undefined          // Previous node in optimal path from source
   dist[source] := 0                     // Distance from source to source
   Q := the set of all nodes in Graph
   // All nodes in the graph are unoptimized - thus are in Q
   while Q is not empty:                 // The main loop
       u := vertex in Q with smallest dist[]
       if dist[u] = infinity:
          break                         // all remaining vertices are inaccessible from source
       remove u from Q
       for each neighbor v of u:         // where v has not yet been removed from Q.
           alt := dist[u] + dist_between(u, v) 
           if alt < dist[v]:             // Relax (u,v,a)
               dist[v] := alt
               previous[v] := u
   return dist[]

ÉDITER:

  1. comme crétin, a déclaré que l'utilisation de http://en.wikipedia.org/wiki/A*_algorithm peut être plus rapide.
  2. le moyen le plus rapide est de pré-calculer toutes les distances et de les enregistrer dans une matrice complète 8x8. eh bien, j'appellerais cela de la triche, et cela ne fonctionne que parce que le problème est petit. Mais parfois, les concours vérifieront la vitesse d'exécution de votre programme.
  3. Le point principal est que si vous vous préparez à un concours de programmation, vous devez connaître les algorithmes courants dont celui de Dijkstra. Un bon point de départ est la lecture de l' Introduction to AlgorithmsISBN 0-262-03384-4. Ou vous pouvez essayer wikipedia, http://en.wikipedia.org/wiki/List_of_algorithms

Cela semble complexe par rapport à la solution de Mustafa ci-dessous.
lpapp

Qu'entendez-vous par déménagement indisponible? Un chevalier peut atteindre n'importe quelle case !?
everlasto

51

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). Coup de chevalier

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

int f( int x , int y )
{
    int delta = x - y;

    if( y > delta )
        return 2 * ( ( y - delta ) / 3 ) + delta;
    else
        return delta - 2 * ( ( delta - y ) / 4 );
}

Remarque: Cette question a été posée lors du SACO 2007 Day 1
et les solutions sont ici


8
Pouvez-vous décrire comment vous avez élaboré cette formule?
kybernetikos

3
Ce code fonctionne-t-il? Si un chevalier est positionné à (0,0) et que je veux le déplacer au point (1,0). Cela satisfait 0 <= y <= x. delta = 1-0 = 1. y n'est pas plus grand que delta (0 <1). Cela signifie que je vais pour l'autre cas. delta - 2 * ((delta - y) / 4) = 1-2 ((1-0) / 4) = 1-1 / 2 = 1. Je ne peux pas déplacer le chevalier de (0,0) à (1,0) en un seul coup. La question est que cet algorithme fonctionne? Ou qu'est-ce que je fais de mal?
SimpleApp

3
On dirait que cela ne fonctionne que pour les positions directes possibles. Mais si l'utilisateur fournit (2,2), il renvoie 0 et si l'utilisateur fournit (4,4), il renvoie 2 qui sont faux.
yunas

6
Cela devrait être 2 * floor((y - delta) / 3) + deltaet delta - 2 * floor((delta - y) / 4). C'est la solution officielle de cette page de concours, mais c'est faux. Cette première équation (de if) 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?
Robo Robok

2
Je suis d'accord que cette solution ne fonctionne pas. Voir d'autres réponses telles que stackoverflow.com/a/26888893/4288232 pour une solution O (1) fonctionnelle.
TimSC

45

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):

Motifs

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:

  • Les groupes verticaux bleus incrémentés de 4
  • Les diagonales rouges "primaires" (elles vont de haut à gauche en bas à droite, comme une barre oblique inverse)
  • Les diagonales vertes "secondaires" (même orientation que le rouge)

(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:

function getMoveCountO1(x, y) {

    var newXY = simplifyBySymmetry(x, y);

    x = newXY.x;
    y = newXY.y;

    var specialMoveCount = getSpecialCaseMoveCount(x ,y);

    if (specialMoveCount !== undefined)
        return specialMoveCount;

    else if (isVerticalCase(x, y))
        return getVerticalCaseMoveCount(x ,y);

    else if (isPrimaryDiagonalCase(x, y))
        return getPrimaryDiagonalCaseMoveCount(x ,y);

    else if (isSecondaryDiagonalCase(x, y))
        return getSecondaryDiagonalCaseMoveCount(x ,y);

}

le plus difficile étant les groupes verticaux:

function isVerticalCase(x, y) {

    return y >= 2 * x;

}

function getVerticalCaseMoveCount(x, y) {

    var normalizedHeight = getNormalizedHeightForVerticalGroupCase(x, y);

    var groupIndex = Math.floor( normalizedHeight / 4);

    var groupStartMoveCount = groupIndex * 2 + x;

    return groupStartMoveCount + getIndexInVerticalGroup(x, y);

}

function getIndexInVerticalGroup(x, y) {

    return getNormalizedHeightForVerticalGroupCase(x, y) % 4;

}

function getYOffsetForVerticalGroupCase(x) {

    return x * 2;

}

function getNormalizedHeightForVerticalGroupCase(x, y) {

    return y - getYOffsetForVerticalGroupCase(x);

}

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).


Cela ne semble pas gérer les cas d'angle (littéraux). Si le «0» est le carré inférieur gauche du plateau (a1), alors vous ne pouvez pas accéder à l'espace «2» (b2) le plus proche en deux coups. Parce que pour ce faire, votre premier mouvement devrait être dans l'espace inexistant à gauche de (a3).
John Hascall

Bien, j'ai changé ma réponse pour inclure l'hypothèse d'un échiquier infini
Graeme Pyle

@JonatasWalker Veuillez expliquer, je ne vois pas de problème entre (8,0) et (0,0). Cela prend 4 coups?
Graeme Pyle

Désolé @GraemePyle, ma faute, en supprimant mon commentaire.
Jonatas Walker

2
salut @GraemePyle - Je suis d'accord avec vous, c'est la meilleure approche de programmation globale. Grand diagramme au fait!
Fattie

22

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 minussigne dans l' flooropérateur, en conséquence ils ont une mauvaise formulefloor(-a) != -floor(a) for any a .

Voici la formule analytique correcte:

var delta = x-y;
if (y > delta) {
    return delta - 2*Math.floor((delta-y)/3);
} else {
    return delta - 2*Math.floor((delta-y)/4);
}

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:

function distance(x,y){
     // axes symmetry 
     x = Math.abs(x);
     y = Math.abs(y);
     // diagonal symmetry 
     if (x < y) {
        t = x;x = y; y = t;
     }
     // 2 corner cases
     if(x==1 && y == 0){
        return 3;
     }
     if(x==2 && y == 2){
        return 4;
     }
    
    // main formula
    var delta = x-y;
		if(y>delta){
  		return delta - 2*Math.floor((delta-y)/3);
  	}
  	else{
  		return delta - 2*Math.floor((delta-y)/4);
  	}
}


$body = $("body");
var html = "";
for (var y = 20; y >= 0; y--){
	html += '<tr>';
	for (var x = 0; x <= 20; x++){
  	html += '<td style="width:20px; border: 1px solid #cecece" id="'+x+'_'+y+'">'+distance(x,y)+'</td>';
  }
  html += '</tr>';
}

html = '<table>'+html+'</table>';
$body.append(html);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Remarque: le jQuery utilisé à titre d'illustration uniquement, pour le code, voir la distancefonction.


2
@OlegAbrazhaev La fonction "distance" est une fonction analytique et peut calculer le nombre de pas pour une position donnée (x, y) en temps O (1). Fondamentalement, dans cette formule, nous ne dépendons pas du conseil d'administration (je suppose que par "tableau infini" vous entendez cette propriété), donc cela fonctionnera.
simon

2
@simon Quelqu'un peut-il expliquer la formule principale s'il vous plaît? Je trouve difficile de pouvoir l'expliquer avec des mots simples
MarBVI

1
@MarBVI Si nous approchons de la ligne y = x, nous pouvons diminuer x + y de 3 à chaque mouvement en restant près de la ligne y = x. Si nous nous approchons de la ligne y = 0, nous pouvons diminuer x de 2 à chaque mouvement en restant près de la ligne y = 0. C'est pourquoi nous avons 2 cas, plus précisément ici ce que j'entends en disant près d'une ligne particulière: 1. Par ligne proche y = x j'entends section restreinte par y = x et y = x / 2 lignes (y> x / 2 ). 2. Par ligne proche de y = 0, j'entends une section limitée par y = 0 et y = x / 2 lignes (y <x / 2). En prenant tout ce qui est mentionné ci-dessus et si nous supprimons Math.floor et simplifions la formule principale, nous obtiendrons la formule suivante: if (y> x / 2) then {return (x + y) / 3} else {return x / 2}
simon

1
@simon super, cela rend les choses plus claires ... ty for your time :)
MarBVI

1
juste au cas où, le concours BAPC2017 avait également une question nommée Knight's Marathon sur une planche infinie que cette formule le résout parfaitement. 2017.bapc.eu/files/preliminaryaries_problems.pdf
Amir-Mousavi

19

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.


1
Est-ce juste moi qui suis confus au sujet de cette réponse? "dessine un carré avec des coins (x-4, y-4), (x-4, y + 4), (x + 4, y-4), (x + 4, y + 4). Cet ensemble (appel it S4) contient 32 points ". Non, il en contient 81 car c'est un carré 9x9? Aussi, "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." Ce n'est pas vrai, prenez par exemple x0 = 0, y0 = 0, x1 = 4, y1 = 4, le chemin le plus court est 4, pas 2 comme le suggère cette formule.
satoshi

1
(1) L'ensemble se réfère uniquement aux points sur la limite du carré lui-même. Cela a 32 points / emplacements. (2) Lorsque vous prenez en compte le PS de l'affiche sur les coups supplémentaires (voir également les commentaires dans le message d'origine), le nombre minimum de coups devient deux.
Steve Tjoa

Merci, cela a du sens maintenant :)
satoshi

et si une planche est infinie? dans ce cas, seul BFS fonctionnera bien
Oleg Abrazhaev

@SteveTjoa désolé, je ne comprends pas pourquoi vous avez mentionné le mouvement (+ -2, + -2), car c'est impossible pour un chevalier
Pavel Bely

12

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):

def solve(x,y):
        x = abs(x)
        y = abs(y)
        if y > x:
            temp=y
            y=x
            x=temp  
        if (x==2 and y==2):
            return 4
        if (x==1 and y==0):
            return 3

    if(y == 0 or float(y) / float(x) <= 0.5):
        xClass = x % 4
        if (xClass == 0):
            initX = x/2
        elif(xClass == 1):
            initX = 1 + (x/2)
        elif(xClass == 2):
            initX = 1 + (x/2)
        else:
            initX = 1 + ((x+1)/2)

        if (xClass > 1):
            return initX - (y%2)
        else:
            return initX + (y%2)
    else:
        diagonal = x - ((x-y)/2)
        if((x-y)%2 == 0):
            if (diagonal % 3 == 0):
                return (diagonal/3)*2
            if (diagonal % 3 == 1):
                return ((diagonal/3)*2)+2
            else:
                return ((diagonal/3)*2)+2
        else:
            return ((diagonal/3)*2)+1


def test():
    real=[
    [0,3,2,3,2,3,4,5,4,5,6,7,6,7],
    [3,2,1,2,3,4,3,4,5,6,5,6,7,8],
    [2,1,4,3,2,3,4,5,4,5,6,7,6,7],
    [3,2,3,2,3,4,3,4,5,6,5,6,7,8],
    [2,3,2,3,4,3,4,5,4,5,6,7,6,7],
    [3,4,3,4,3,4,5,4,5,6,5,6,7,8],
    [4,3,4,3,4,5,4,5,6,5,6,7,6,7],
    [5,4,5,4,5,4,5,6,5,6,7,6,7,8],
    [4,5,4,5,4,5,6,5,6,7,6,7,8,7],
    [5,6,5,6,5,6,5,6,7,6,7,8,7,8],
    [6,5,6,5,6,5,6,7,6,7,8,7,8,9],
    [7,6,7,6,7,6,7,6,7,8,7,8,9,8]]

    for x in range(12):
        for y in range(12):
            res = solve(x,y)
            if res!= real[x][y]:
                print (x, y), "failed, and returned", res, "rather than", real[x][y]
            else:
               print (x, y), "worked. Cool!"

test()

10
Se référer aux réponses comme aboveou belowne fonctionne pas vraiment sur SO.
Petr Peller

1
Voici ma version en python 2/3. J'ai essayé de simplifier la fonction de résolution mais ce n'est pas facile! gist.github.com/TimSC/8b9a80033f3a22a708a4b9741931c591
TimSC

9

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.


1
+ !, pour ce problème spécifique, BFS suffit.
TiansHUo

3
BFS pourrait être suffisant, mais un BST simple explosera pour de nombreuses requêtes - vous devrez mettre en cache les cases que vous avez visitées. Et puis BFS commence à ressembler un peu à l'algorithme de Dijkstra ...
Charles Stewart

Quel serait le meilleur moyen de suivre toute la position que nous avons déjà parcourue pour que l'arborescence BFS ne se développe que vers l'avant et lorsque nous découvrons des nœuds disponibles à partir d'un nouveau point, nous ne finissons pas par ajouter à nouveau l'ancien nœud ... et rester coincé dans une boucle infinie!
Nitish Upreti

Je suppose ici que nous pouvons simplement le faire en stockant notre dernière position de chevalier?
Nitish Upreti

7

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):

2 1 4 3
3 2 1 2
0 3 2 3

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:

  • Ces séquences sont les chemins les plus courts. (Voulez-vous que je le prouve, ou est-ce évident?)
  • Nous ne nous soucions que du nombre de ces mouvements. Nous pouvons mélanger et assortir les mouvements dans n'importe quel ordre.

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:

  • La formule au-dessus de la ligne médiane déplace (x, y) vers l'un des éléments (0,0), (1,1) ou (2,2).
  • La formule au-dessous de la ligne médiane déplace (x, y) vers l'un des éléments (0,0), (1,0), (2,0) ou (1,1).

(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:

. . 4
. 2 .
0 3 2

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:

  • Nous avons commencé là-bas, ou
  • Nous nous sommes déplacés là-bas, par une séquence de mouvements de 8 heures et 10 heures. Mais si le dernier coup était à 8 heures (ce qu'il a le droit d'être, puisque nous pouvons faire nos coups dans n'importe quel ordre), alors nous devons être passés par (3,1), dont la distance est en fait de 2 (comme vous pouvez voir de la feuille de triche originale). Donc, ce que nous devrions faire est de revenir en arrière d'un coup de 8 heures, en économisant deux coups au total.

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:

. . 4
. 2 . 2
0 3 2

(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:

def knightDistance (x, y):
    # normalise the coordinates
    x, y = abs(x), abs(y)
    if (x<y): x, y = y, x
    # now 0 <= y <= x

    # n8 means (-2,-1) (8 o'clock), n7 means (-1,-2) (7 o'clock), n10 means (-2,+1) (10 o'clock)
    if (x>2*y):
        # we're below the midline.  Using 8- & 10-o'clock moves
        n7, n8, n10 = 0,  (x + 2*y)//4,  (x - 2*y + 1)//4
    else:
        # we're above the midline.  Using 7- and 8-o'clock moves
        n7, n8, n10 = (2*y - x)//3, (2*x - y)//3,  0
    x -= 2*n8 + n7 + 2*n10
    y -= n8 + 2*n7 - n10
    # now 0<=x<=2, and y <= x.  Also (x,y) != (2,1)

    # Try to optimise the paths.
    if (x, y)==(1, 0): # hit the  3.  Did we need to?
        if (n8>0): # could have passed through the 2 at 3,1.  Back-up
            x, y = 3, 1; n8-=1;
    if (x, y)==(2, 2): # hit the 4.  Did we need to?
        if (n8>0): # could have passed through a 3 at 4,3.  Back-up, and take 7 o'clock to 2 at 3,1
            x, y = 3, 1; n8-=1; n7+=1

    # Almost there.  Now look up the final leg
    cheatsheet = [[0, 3, 2], [2, None, 2], [4]]
    return n7 + n8 + n10 + cheatsheet [y][x-y]

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:

def validate (n):

    def isSquareReasonable (x, y):
        d, downhills = knightDistance (x, y), 0
        moves = [(1, 2), (2, 1), (2, -1), (1, -2), (-1, -2), (-2, -1), (-2, 1), (-1,  2)]
        for dx, dy in moves:
            dd = knightDistance (x+dx,  y+dy)
            if (dd == d+1): pass
            elif (dd== d-1): downhills += 1
            else: return False;
        return (downhills>0) or (d==0)

    for x in range (0,  n+1):
        for y in range (0,  n+1):
            if not isSquareReasonable (x,  y): raise RuntimeError ("Validation failed")

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?


2
/*
This program takes two sets of cordinates on a 8*8 chessboard, representing the
starting and ending points of a knight's path.
The problem is to print the cordinates that the knight traverses in between, following
the shortest path it can take.
Normally this program is to be implemented using the Djikstra's algorithm(using graphs)
but can also be implemented using the array method.
NOTE:Between 2 points there may be more than one shortest path. This program prints
only one of them.
*/

#include<stdio.h>

#include<stdlib.h>

#include<conio.h>

int m1=0,m2=0;

/*
This array contains three columns and 37 rows:
The rows signify the possible coordinate differences.
The columns 1 and 2 contains the possible permutations of the row and column difference 
between two positions on a chess board;
The column 3 contains the minimum number of steps involved in traversing the knight's 
path with the given permutation*/

int arr[37][3]={{0,0,0},{0,1,3},{0,2,2},{0,3,3},{0,4,2},{0,5,3},{0,6,4},{0,7,5},    {1,1,2},{1,2,1},{1,3,2},{1,4,3},{1,5,4},{1,6,3},{1,7,4},{2,2,4},{2,3,3},{2,4,2},
            {2,5,3},{2,6,3},{2,7,5},{3,3,2},{3,4,3},{3,5,4},{3,6,3},{3,7,4},{4,4,4},{4,5,3},{4,6,4},{4,7,5},{5,5,4},{5,6,5},{5,7,4},{6,6,5},{6,7,5},{7,7,6}};

void printMoves(int,int,int,int,int,int);
void futrLegalMove(int,int,int,int);
main()
{
  printf("KNIGHT'S SHORTEST PATH ON A 8*8 CHESSBOARD :\n");
  printf("------------------------------------------");
  printf("\nThe chessboard may be treated as a 8*8 array here i.e. the (1,1) ");
  printf("\non chessboard is to be referred as (0,0) here and same for (8,8) ");
  printf("\nwhich is to be referred as (7,7) and likewise.\n");
  int ix,iy,fx,fy;
  printf("\nEnter the initial position of the knight :\n");
  scanf("%d%d",&ix,&iy);
  printf("\nEnter the final position to be reached :\n");
  scanf("%d%d",&fx,&fy);
  int px=ix,py=iy;
  int temp;
  int tx,ty;
  printf("\nThe Knight's shortest path is given by :\n\n");
  printf("(%d, %d)",ix,iy);
  futrLegalMove(px,py,m1,m2);
  printMoves(px,py,fx,fy,m1,m2);
   getch();
} 

/*
  This method checkSteps() checks the minimum number of steps involved from current
  position(a & b) to final position(c & d) by looking up in the array arr[][].
*/

int checkSteps(int a,int b,int c,int d)
{  
    int xdiff, ydiff;
    int i, j;
    if(c>a)
        xdiff=c-a;
    else
        xdiff=a-c;
    if(d>b)
        ydiff=d-b;
    else
        ydiff=b-d;
    for(i=0;i<37;i++)
        {
            if(((xdiff==arr[i][0])&&(ydiff==arr[i][1])) || ((xdiff==arr[i][1])&& (ydiff==arr[i] [0])))
            {
                j=arr[i][2];break;
            }
        }

        return j;
}   

/*
This method printMoves() prints all the moves involved.
*/

void printMoves(int px,int py, int fx, int fy,int a,int b)
{    
 int temp;
 int tx,ty;
 int t1,t2;
  while(!((px==fx) && (py==fy)))
  {   
      printf(" --> ");
      temp=checkSteps(px+a,py+b,fx,fy);
      tx=px+a;
      ty=py+b;
      if(!(a==2 && b==1))
      {if((checkSteps(px+2,py+1,fx,fy)<temp) && checkMove(px+2,py+1))
      {temp=checkSteps(px+2,py+1,fx,fy);
       tx=px+2;ty=py+1;}}
      if(!(a==2 && b==-1))
      {if((checkSteps(px+2,py-1,fx,fy)<temp) && checkMove(px+2,py-1))
      {temp=checkSteps(px+2,py-1,fx,fy);
       tx=px+2;ty=py-1;}}
      if(!(a==-2 && b==1))
      {if((checkSteps(px-2,py+1,fx,fy)<temp) && checkMove(px-2,py+1))
      {temp=checkSteps(px-2,py+1,fx,fy);
       tx=px-2;ty=py+1;}}
      if(!(a==-2 && b==-1))
      {if((checkSteps(px-2,py-1,fx,fy)<temp) && checkMove(px-2,py-1))
      {temp=checkSteps(px-2,py-1,fx,fy);
       tx=px-2;ty=py-1;}}
      if(!(a==1 && b==2))
      {if((checkSteps(px+1,py+2,fx,fy)<temp) && checkMove(px+1,py+2))
      {temp=checkSteps(px+1,py+2,fx,fy);
       tx=px+1;ty=py+2;}}
      if(!(a==1 && b==-2))
      {if((checkSteps(px+1,py-2,fx,fy)<temp) && checkMove(px+1,py-2))
      {temp=checkSteps(px+1,py-2,fx,fy);
       tx=px+1;ty=py-2;}}
      if(!(a==-1 && b==2))
      {if((checkSteps(px-1,py+2,fx,fy)<temp) && checkMove(px-1,py+2))
      {temp=checkSteps(px-1,py+2,fx,fy);
       tx=px-1;ty=py+2;}}
      if(!(a==-1 && b==-2))
      {if((checkSteps(px-1,py-2,fx,fy)<temp) && checkMove(px-1,py-2))
      {temp=checkSteps(px-1,py-2,fx,fy);
       tx=px-1;ty=py-2;}}
       t1=tx-px;//the step taken in the current move in the x direction.
       t2=ty-py;//" " " " " " " " " " " " " " " " " " " " " y " " " " ".
       px=tx;
       py=ty;
       printf("(%d, %d)",px,py);
       futrLegalMove(px,py,t1,t2);
       a=m1;
       b=m2;
   }

} 

/*
The method checkMove() checks whether the move in consideration is beyond the scope of
board or not.
*/   

int checkMove(int a, int b)
{
    if(a>7 || b>7 || a<0 || b<0)
        return 0;
    else
        return 1;
}

/*Out of the 8 possible moves, this function futrLegalMove() sets the valid move by
  applying the following constraints
      1. The next move should not be beyond the scope of the board.
      2. The next move should not be the exact opposite of the previous move.
  The 1st constraint is checked by sending all possible moves to the checkMove() 
  method;
  The 2nd constraint is checked by passing as parameters(i.e. a and b) the steps of the 
  previous move and checking whether or not it is the exact opposite of the current move.
*/

void futrLegalMove(int px,int py,int a,int b)
{
     if(checkMove(px+2,py+1) && (a!=-2 && b!=-1))
         m1=2,m2=1;
     else
     {
         if(checkMove(px+2,py-1)&& (a!=-2 && b!=1))
             m1=2,m2=-1;
     else
     {
         if(checkMove(px-2,py+1)&& (a!=2 && b!=-1))
              m1=-2,m2=1;
     else
     {
         if(checkMove(px-2,py-1)&& (a!=2 && b!=1))
               m1=-2,m2=-1;
     else
     {
         if(checkMove(px+1,py+2)&& (b!=-2 && a!=-1))
               m2=2,m1=1;
     else
     {
         if(checkMove(px+1,py-2)&& (a!=-1 && b!=2))
               m2=-2,m1=1;
     else
     {
         if(checkMove(px-1,py+2)&& (a!=1 && b!=-2))
               m2=2,m1=-1;
     else
     {
         if(checkMove(px-1,py-2)&& (a!=1 && b!=2))
               m2=-2,m1=-1;
     }}}}}}}
}

//End of Program.

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).


2

Je pense que cela pourrait aussi vous aider.

NumWays(x,y)=1+min(NumWays(x+-2,y-+1),NumWays(x+-1,y+-2)); 

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.


1

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.

#!/usr/local/bin/perl -w

use strict;

my $from = [0,0];
my $to   = [7,7];

my $f_from = flat($from);
my $f_to   = flat($to);

my $max_x = 7;
my $max_y = 7;
my @moves = ([-1,2],[1,2],[2,1],[2,-1],[1,-2],[-1,-2],[-2,-1],[-2,1]);
my %squares = ();
my $i = 0;
my $min = -1;

my @s = ( $from );

while ( @s ) {

   my @n = ();
   $i++;

   foreach my $s ( @s ) {
       unless ( $squares{ flat($s) } ) {
            my @m = moves( $s );
            push @n, @m;
            $squares{ flat($s) } = { i=>$i, n=>{ map {flat($_)=>1} @m }, };

            $min = $i if $squares{ flat($s) }->{n}->{$f_to};
       }
   }

   last if $min > -1;
   @s = @n;
}

show_path( $f_to, $min );

sub show_path {
    my ($s,$i) = @_;

    return if $s eq $f_from;

    print "$i => $f_to\n" if $i == $min;

    foreach my $k ( keys %squares ) {
       if ( $squares{$k}->{i} == $i && $squares{$k}->{n}->{$s} ) {
            $i--;
            print "$i => $k\n";
            show_path( $k, $i );
            last;
       }
    }
}

sub flat { "$_[0]->[0],$_[0]->[1]" }

sub moves {
    my $c = shift;
    my @s = ();

    foreach my $m ( @moves ) {
       my $x = $c->[0] + $m->[0];
       my $y = $c->[1] + $m->[1];

       if ( $x >= 0 && $x <=$max_x && $y >=0 && $y <=$max_y) {
           push @s, [$x, $y];
       }
    }
    return @s;
}

__END__

1
public class Horse {

    private int[][] board;
    private int[] xer = { 2, 1, -1, -2, -2, -1, 1, 2 };
    private int[] yer = { 1, 2, 2, 1, -1, -2, -2, -1 };
    private final static int A_BIG_NUMBER = 10000;
    private final static int UPPER_BOUND = 64;


    public Horse() {
        board =  new int[8][8];
    }

    private int solution(int x, int y, int destx, int desty, int move) {

        if(move == UPPER_BOUND) {
            /* lets put an upper bound to avoid stack overflow */
            return A_BIG_NUMBER;
        }

        if(x == 6 && y ==5) {
            board[6][5] = 1;
            return 1;
        }
        int min = A_BIG_NUMBER;
        for (int i = 0 ; i < xer.length; i++) {
            if (isMoveGood(x + xer[i], y + yer[i])) {
                if(board[x + xer[i]][y + yer[i]] != 0) {
                    min = Integer.min(min, 1 + board[x +xer[i]] [y +yer[i]]);                   
                } else {
                    min = Integer.min(min, 1 + solution(x + xer[i], y + yer[i], destx, desty, move + 1));   
                }                   
            }
        }   
        board[x][y] = min;
        return min;
    }


    private boolean isMoveGood(int x, int y) {
        if (x >= 0 && x < board.length && y >= 0 && y < board.length)
            return true;
        return false;
    }


    public static void main(String[] args) {

        int destX = 6;
        int destY = 7;
        final Horse h = new Horse();
        System.out.println(h.solution(0, 0, destX, destY, 0));
    }
}

0

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:

def getBoardOffset(board)
  return board.length / 2
end

def setMoveCount(x, y, count, board)
  offset = getBoardOffset(board)
  board[y + offset][x + offset] = count
end

def getMoveCount(x, y, board)
    offset = getBoardOffset(board)
    row = board[y + offset]
    return row[x + offset]
end

def isBottomOfVerticalCase(x, y)
    return (y - 2 * x) % 4 == 0
end

def isPrimaryDiagonalCase(x, y)
    return (x + y) % 2 == 0
end

def isSecondaryDiagonalCase(x, y)
    return (x + y) % 2 == 1
end

def simplifyBySymmetry(x, y)
    x = x.abs
    y = y.abs
    if (y < x)
      t = x
      x = y
      y = t
    end
    return {x: x, y: y}
end

def getPrimaryDiagonalCaseMoveCount(x, y)
    var diagonalOffset = y + x
    var diagonalIntersect = diagonalOffset / 2
    return ((diagonalIntersect + 2) / 3).floor * 2
end

def getSpecialCaseMoveCount(x, y)
    specials = [{
            x: 0,
            y: 0,
            d: 0
        },
        {
            x: 0,
            y: 1,
            d: 3
        },
        {
            x: 0,
            y: 2,
            d: 2
        },
        {
            x: 0,
            y: 3,
            d: 3
        },
        {
            x: 2,
            y: 2,
            d: 4
        },
        {
            x: 1,
            y: 1,
            d: 2
        },
        {
            x: 3,
            y: 3,
            d: 2
        }
    ];
    matchingSpecial=nil
    specials.each do |special|
      if (special[:x] == x && special[:y] == y)
        matchingSpecial = special
      end
    end
    if (matchingSpecial)
      return matchingSpecial[:d]
    end
end

def isVerticalCase(x, y)
  return y >= 2 * x
end

def getVerticalCaseMoveCount(x, y)
    normalizedHeight = getNormalizedHeightForVerticalGroupCase(x, y)
    groupIndex = (normalizedHeight/4).floor
    groupStartMoveCount = groupIndex * 2 + x
    return groupStartMoveCount + getIndexInVerticalGroup(x, y)
end

def getIndexInVerticalGroup(x, y)
    return getNormalizedHeightForVerticalGroupCase(x, y) % 4
end

def getYOffsetForVerticalGroupCase(x) 
    return x * 2
end

def getNormalizedHeightForVerticalGroupCase(x, y)
    return y - getYOffsetForVerticalGroupCase(x)
end

def getSecondaryDiagonalCaseMoveCount(x, y)
    diagonalOffset = y + x
    diagonalIntersect = diagonalOffset / 2 - 1
    return ((diagonalIntersect + 2) / 3).floor * 2 + 1
end

def getMoveCountO1(x, y)
    newXY = simplifyBySymmetry(x, y)
    x = newXY[:x]
    y = newXY[:y]
    specialMoveCount = getSpecialCaseMoveCount(x ,y)
    if (specialMoveCount != nil)
      return specialMoveCount
    elsif (isVerticalCase(x, y))
      return getVerticalCaseMoveCount(x ,y)
    elsif (isPrimaryDiagonalCase(x, y))
      return getPrimaryDiagonalCaseMoveCount(x ,y)
    elsif (isSecondaryDiagonalCase(x, y))
      return getSecondaryDiagonalCaseMoveCount(x ,y)
    end
end

def solution(x ,y)
  return getMoveCountO1(x, y)
end


puts solution(0,0)

La seule intention est de faire gagner du temps à quelqu'un pour convertir le code si quelqu'un a besoin du code complet.


0

voici la version PHP de la fonction de Jules May

function knightDistance($x, $y)
{
    $x = abs($x);
    $y = abs($y);

    if($x < $y)
    {
        $tmp = $x;
        $x = $y;
        $y = $tmp;
    }

    if($x > 2 * $y)
    {
        $n7 = 0;
        $n8 = floor(($x + 2*$y) / 4);
        $n10 = floor(($x - 2*$y +1) / 4);
    }
    else
    {
        $n7 = floor((2*$y - $x) / 3);
        $n8 = floor((2*$x - $y) / 3);
        $n10 = 0;
    }

    $x -= 2 * $n8 + $n7 + 2 * $n10;
    $y -= $n8 + 2 * $n7 - $n10;

    if($x == 1 && $y == 0)
    {
        if($n8 > 0)
        {
            $x = 3;
            $y = 1;
            $n8--;
        }
    }
    if($x == 2 && $y == 2)
    {
        if($n8 > 0)
        {
            $x = 3;
            $y = 1;
            $n8--;
            $n7++;
        }
    }

    $cheatsheet = [[0, 3, 2], [2, 0, 2], [4]];

    return $n7 + $n8 + $n10 + $cheatsheet [$y][$x-$y];
}

0

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.

public class KnightKing2 {
    private static int tempCount = 0;

    public static void main(String[] args) throws IOException {
        Scanner in = new Scanner(System.in);
        int ip1 = Integer.parseInt(in.nextLine().trim());
        int ip2 = Integer.parseInt(in.nextLine().trim());
        int ip3 = Integer.parseInt(in.nextLine().trim());
        int ip4 = Integer.parseInt(in.nextLine().trim());
        in.close();
        int output = getStepCount(ip1, ip2, ip3, ip4);
        System.out.println("Shortest Path :" + tempCount);

    }

    // 2 1 6 5 -> 4
    // 6 6 5 5 -> 2

    public static int getStepCount(int input1, int input2, int input3, int input4) {
        return recurse(0, input1, input2, input3, input4);

    }

    private static int recurse(int count, int tx, int ty, int kx, int ky) {

        if (isSolved(tx, ty, kx, ky)) {
            int ccount = count+1;
            System.out.println("COUNT: "+count+"--"+tx+","+ty+","+ccount);
            if((tempCount==0) || (ccount<=tempCount)){
                tempCount = ccount;
            }
            return ccount;
        }

            if ((tempCount==0 || count < tempCount) && ((tx < kx+2) && (ty < ky+2))) {
                if (!(tx + 2 > 8) && !(ty + 1 > 8)) {
                    rightTop(count, tx, ty, kx, ky);

                }
                if (!(tx + 2 > 8) && !(ty - 1 < 0)) {
                    rightBottom(count, tx, ty, kx, ky);
                }
                if (!(tx + 1 > 8) && !(ty + 2 > 8)) {
                    topRight(count, tx, ty, kx, ky);
                }
                if (!(tx - 1 < 0) && !(ty + 2 > 8)) {
                    topLeft(count, tx, ty, kx, ky);
                }
                if (!(tx + 1 > 8) && !(ty - 2 < 0)) {
                     bottomRight(count, tx, ty, kx, ky);
                }
                if (!(tx - 1 < 0) && !(ty - 2 < 0)) {
                     bottomLeft(count, tx, ty, kx, ky);
                }
                if (!(tx - 2 < 0) && !(ty + 1 > 8)) {
                    leftTop(count, tx, ty, kx, ky);
                }
                if (!(tx - 2 < 0) && !(ty - 1 < 0)) {
                    leftBottom(count, tx, ty, kx, ky);
                }
            }

        return count;

    }

    private static int rightTop(int count, int tx, int ty, int kx, int ky) {
        return count + recurse(count + 1, tx + 2, ty + 1, kx, ky);

    }

    private static int topRight(int count, int tx, int ty, int kx, int ky) {
        return count + recurse(count + 1, tx + 1, ty + 2, kx, ky);
    }

    private static int rightBottom(int count, int tx, int ty, int kx, int ky) {
        return count + recurse(count + 1, tx + 2, ty - 1, kx, ky);
    }

    private static int bottomRight(int count, int tx, int ty, int kx, int ky) {
        return count + recurse(count + 1, tx + 1, ty - 2, kx, ky);
    }

    private static int topLeft(int count, int tx, int ty, int kx, int ky) {
        return count + recurse(count + 1, tx - 1, ty + 2, kx, ky);
    }

    private static int bottomLeft(int count, int tx, int ty, int kx, int ky) {
        return count + recurse(count + 1, tx - 1, ty - 2, kx, ky);
    }

    private static int leftTop(int count, int tx, int ty, int kx, int ky) {
        return count + recurse(count + 1, tx - 2, ty + 1, kx, ky);
    }

    private static int leftBottom(int count, int tx, int ty, int kx, int ky) {
        return count + recurse(count + 1, tx - 2, ty - 1, kx, ky);
    }

    private static boolean isSolved(int tx, int ty, int kx, int ky) {
        boolean solved = false;
        if ((tx == kx) && (ty == ky)) {
            solved = true;
        } else if ((tx + 2 == kx) && (ty + 1 == ky)) { // right top
            solved = true;
        } else if ((tx + 2 == kx) && (ty - 1 == ky)) { // right bottom
            solved = true;
        } else if ((ty + 2 == ky) && (tx + 1 == kx)) {// top right
            solved = true;
        } else if ((ty + 2 == ky) && (tx - 1 == kx)) {// top left
            solved = true;
        } else if ((tx - 2 == kx) && (ty + 1 == ky)) { // left top
            solved = true;
        } else if ((tx - 2 == kx) && (ty - 1 == ky)) {// left bottom
            solved = true;
        } else if ((ty - 2 == ky) && (tx + 1 == kx)) { // bottom right
            solved = true;
        } else if ((ty - 2 == ky) && (tx - 1 == kx)) { // bottom left
            solved = true;
        }

        return solved;
    }

}

1
Il peut être encore optimisé pour éviter les doublons.
Arun

-1

Voici une version C basée sur le code Mustafa Serdar Şanlı qui fonctionne pour une carte finit:

#include <stdio.h>
#include <math.h>

#define test(x1, y1, x2, y2) (sx == x1 && sy == y1 &&tx == x2 &&ty == y2) || (sx == x2 && sy == y2 && tx == x1 && ty==y1)

int distance(int sx, int sy, int tx, int ty) {
    int x, y, t;
    double delta;

    // special corner cases 
    if (test(1, 1, 2, 2) || 
        test(7, 7, 8, 8) || 
        test(7, 2, 8, 1) || 
        test(1, 8, 2, 7))
        return 4;

    // axes symmetry 
    x = abs(sx - tx);
    y = abs(sy - ty);

    // diagonal symmetry 
    if (x < y) {
        t = x;
        x = y;
        y = t;
    }

    // 2 corner cases
    if (x == 1 && y == 0)
        return 3;
    if (x == 2 && y == 2)
        return 4;

    // main
    delta = x - y;
    if (y > delta) {
        return (int)(delta - 2 * floor((delta - y) / 3));
    }
    else {
        return (int)(delta - 2 * floor((delta - y) / 4));
    }
}

Testez-le ici avec une preuve contre une solution récursive


1
Tester un nombre fini de cas n'est pas une preuve.
BlenderBender
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.