Ritter kürzester Weg auf Schachbrett

95

Ich habe für einen bevorstehenden Programmierwettbewerb geübt und bin auf eine Frage gestoßen, die mich nur völlig verwirrt. Ich habe jedoch das Gefühl, dass es ein Konzept ist, das ich jetzt lernen sollte, anstatt die Daumen zu drücken, dass es nie auftaucht.

Grundsätzlich handelt es sich um eine Ritterfigur auf einem Schachbrett. Sie erhalten zwei Eingaben: Startort und Endort. Das Ziel ist es, den kürzesten Weg zu berechnen und auszudrucken, den der Ritter nehmen kann, um zum Zielort zu gelangen.

Ich habe mich nie mit Dingen befasst, die dem kürzesten Weg ähneln, und ich weiß nicht einmal, wo ich anfangen soll. Welche Logik verwende ich, um dies anzugehen?

PS Wenn es von Bedeutung ist, möchten sie, dass Sie die normalen Bewegungen des Ritters ergänzen, indem Sie ihm erlauben, sich zu den vier Ecken des Quadrats zu bewegen, die durch die (möglicherweise) acht Bewegungen gebildet werden, die ein Ritter ausführen kann, vorausgesetzt, die Mitte des Quadrats ist der Standort des Ritters.

Kyle Hughes
quelle
Könnten Sie die PS klären? Sie meinen, wenn ein Ritter auf E4 ist, kann er sich zu C2, C6, G2 und G6 bewegen?
Steve Tjoa
Ja, zusätzlich zu den normalen Bewegungen.
Kyle Hughes
1
Hier ist eine mathematische Analyse des Problems: math.stackexchange.com/questions/104700/…
Graeme Pyle

Antworten:

28

Sie haben hier ein Diagramm, in dem alle verfügbaren Züge verbunden sind (Wert = 1) und nicht verfügbare Züge getrennt sind (Wert = 0). Die Matrix mit geringer Dichte lautet wie folgt:

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

Der kürzeste Weg von zwei Punkten in einem Diagramm kann mithilfe von http://en.wikipedia.org/wiki/Dijkstra's_algorithm gefunden werden

Pseudocode von der Wikipedia-Seite:

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[]

BEARBEITEN:

  1. Als Idiot kann die Verwendung des http://en.wikipedia.org/wiki/A*_algorithm schneller sein.
  2. Der schnellste Weg besteht darin, alle Entfernungen vorab zu berechnen und in einer 8x8-Vollmatrix zu speichern. Nun, ich würde das Betrug nennen und funktioniert nur, weil das Problem klein ist. Aber manchmal überprüfen Wettbewerbe, wie schnell Ihr Programm läuft.
  3. Der wichtigste Punkt ist, dass Sie, wenn Sie sich auf einen Programmierwettbewerb vorbereiten, gängige Algorithmen kennen müssen, einschließlich der von Dijkstra. Ein guter Ausgangspunkt ist das Lesen der Introduction to AlgorithmsISBN 0-262-03384-4. Oder versuchen Sie es mit Wikipedia, http://en.wikipedia.org/wiki/List_of_algorithms
TiansHUo
quelle
Dies scheint im Vergleich zu Mustafas unten stehender Lösung komplex zu sein.
lpapp
Was meinst du mit nicht verfügbarem Umzug? Ein Ritter kann jedes Feld erreichen, oder?
Everlasto
51

EDIT: Siehe Simons Antwort , in der er die hier vorgestellte Formel korrigierte.

Tatsächlich gibt es eine O (1) -Formel

Dies ist ein Bild, das ich gemacht habe, um es zu visualisieren (Quadrate, die ein Ritter am N- ten erreichen kann Zug , sind mit derselben Farbe gemalt). Ritterzug

Kannst du das Muster hier bemerken?

Obwohl wir das Muster sehen können, ist es wirklich schwierig, die Funktion zu finden, die f( x , y )die Anzahl der Bewegungen zurückgibt, die erforderlich sind, um vom Quadrat zu gehen( 0 , 0 ) zu Quadrat zu gelangen( x , y )

Aber hier ist die Formel, die funktioniert, wenn 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 );
}

Hinweis: Diese Frage wurde am ersten Tag von SACO 2007 gestellt. Hier finden Sie
Lösungen

Mustafa Serdar Şanlı
quelle
8
Gibt es eine Möglichkeit, zu beschreiben, wie Sie diese Formel ausgearbeitet haben?
Kybernetikos
3
Funktioniert dieser Code? Wenn sich ein Ritter in Position (0,0) befindet und ich ihn auf Punkt (1,0) verschieben möchte. Dies erfüllt 0 <= y <= x. Delta = 1-0 = 1. y ist nicht größer als Delta (0 <1). Das heißt, ich gehe für den anderen Fall. Delta - 2 * ((Delta - y) / 4) = 1-2 ((1-0) / 4) = 1-1 / 2 = 1. Das ist kein Grund, warum ich Ritter in einem Zug von (0,0) nach (1,0) bewegen kann. Die Frage ist, ob dieser Algorithmus funktioniert. Oder was mache ich falsch?
SimpleApp
3
Scheint, als würde es nur für direkt mögliche Positionen funktionieren. Wenn der Benutzer jedoch (2,2) angibt, wird 0 zurückgegeben, und wenn der Benutzer (4,4) angibt, werden 2 zurückgegeben, die falsch sind.
Yunas
6
Es sollte 2 * floor((y - delta) / 3) + deltaund sein delta - 2 * floor((delta - y) / 4). Dies ist eine offizielle Lösung von dieser Wettbewerbsseite, aber sie ist falsch. Diese erste Gleichung (von if) gibt falsche Antworten zurück. Auf dem Schachbrett [-1000..1000] x [-1000..1000], das 2001x2001 groß (aber logisch unbegrenzt) ist, zählt die angegebene Antwort 2.669.329 von 4.004.001 korrekten Feldern (66,66%). Kennt jemand die funktionierende Lösung ohne Schleifen?
Robo Robok
2
Ich bin damit einverstanden, dass diese Lösung nicht funktioniert. Weitere funktionierende O (1) -Lösungen finden Sie in anderen Antworten wie stackoverflow.com/a/26888893/4288232 .
TimSC
45

Hier ist eine korrekte O (1) -Lösung, aber für den Fall, dass sich der Ritter nur wie ein Schachritter und auf einem unendlichen Schachbrett bewegt:

https://jsfiddle.net/graemian/5qgvr1ba/11/

Der Schlüssel, um dies zu finden, besteht darin, die Muster zu bemerken, die beim Zeichnen der Tafel auftreten. In der folgenden Abbildung ist die Anzahl auf dem Quadrat die Mindestanzahl von Zügen, die erforderlich sind, um dieses Quadrat zu erreichen (Sie können die Breitensuche verwenden, um dies zu finden):

Muster

Da die Lösung über die Achsen und Diagonalen symmetrisch ist, habe ich nur den Fall x> = 0 und y> = x gezeichnet.

Der untere linke Block ist die Startposition und die Zahlen in den Blöcken geben die minimale Anzahl von Zügen an, um diese Blöcke zu erreichen.

Es gibt 3 Muster zu beachten:

  • Die inkrementellen blauen vertikalen Gruppen von 4
  • Die "primären" roten Diagonalen (sie verlaufen von oben links nach unten rechts wie ein Backslash)
  • Die "sekundären" grünen Diagonalen (gleiche Ausrichtung wie rot)

(Stellen Sie sicher, dass Sie beide Diagonalsätze von oben links nach unten rechts sehen. Sie haben eine konstante Bewegungsanzahl. Die Diagonalen unten links oben rechts sind viel komplexer.)

Sie können jeweils Formeln ableiten. Die gelben Blöcke sind Sonderfälle. So wird die Lösung:

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

}

Am schwierigsten sind die vertikalen Gruppen:

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

}

Siehe die Geige für die anderen Fälle.

Vielleicht gibt es einfachere oder elegantere Muster, die ich vermisst habe? Wenn ja, würde ich sie gerne sehen. Insbesondere bemerke ich einige diagonale Muster in den blauen vertikalen Fällen, aber ich habe sie nicht untersucht. Unabhängig davon erfüllt diese Lösung immer noch die O (1) -Beschränkung.

Graeme Pyle
quelle
Dies scheint die (wörtlichen) Eckfälle nicht zu behandeln. Wenn die "0" das untere linke Quadrat auf dem Brett (a1) ist, können Sie nicht in zwei Zügen zum nächsten "2" -Raum (b2) gelangen. Denn dazu müsste Ihr erster Schritt in den nicht vorhandenen Raum links von (a3) ​​erfolgen.
John Hascall
Richtig, ich habe meine Antwort geändert, um die unendliche Schachbrettannahme aufzunehmen
Graeme Pyle
@ JonatasWalker Bitte erklären Sie, ich sehe kein Problem von (8,0) nach (0,0). Es dauert 4 Züge?
Graeme Pyle
Sorry @GraemePyle, meine Schuld, mein Kommentar entfernt.
Jonatas Walker
2
hi @GraemePyle - ich stimme dir zu, dies ist der beste Gesamtprogrammieransatz. Tolles Diagramm übrigens!
Fattie
22

Sehr interessantes Problem, auf das ich kürzlich gestoßen bin. Nachdem ich einige Lösungen gesucht hatte, wurde versucht, O(1) time and space complexitydie in SACO 2007 Day 1- Lösungen angegebene analytische Formel ( ) wiederherzustellen .

Zunächst möchte ich Graeme Pyle für eine sehr schöne Visualisierung schätzen, die mir geholfen hat, die Formel zu korrigieren.

Aus irgendeinem Grund (vielleicht zur Vereinfachung oder Schönheit oder nur wegen eines Fehlers) haben sie das minusZeichen in den floorOperator verschoben , was dazu führt, dass sie eine falsche Formel habenfloor(-a) != -floor(a) for any a .

Hier ist die richtige analytische Formel:

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

Die Formel funktioniert für alle (x, y) Paare (nach Anwendung von Achsen und diagonaler Symmetrie) mit Ausnahme von (1,0) und (2,2) Eckfällen, die dem Muster nicht entsprechen und im folgenden Snippet fest codiert sind:

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>

Hinweis: Die nur zur Veranschaulichung verwendete jQuery, Code siehe distanceFunktion.

Simon
quelle
2
@OlegAbrazhaev Die "Distanz" -Funktion ist eine analytische Funktion und kann die Anzahl der Schritte für die gegebene (x, y) Position in O (1) -Zeit berechnen. Grundsätzlich sind wir in dieser Formel nicht von Board abhängig (ich denke, mit "Infinite Board" meinen Sie diese Eigenschaft), daher wird es funktionieren.
Simon
2
@simon Kann jemand bitte die Hauptformel erklären? Es
fällt mir
1
@MarBVI Wenn wir uns der Linie y = x nähern, können wir x + y bei jeder Bewegung um 3 verringern, indem wir in der Nähe der Linie y = x bleiben. Wenn wir uns der Linie y = 0 nähern, können wir x bei jeder Bewegung um 2 verringern, indem wir in der Nähe der Linie y = 0 bleiben. Deshalb haben wir zwei Fälle, genauer gesagt, was ich damit meine, dass ich nahe an einer bestimmten Linie sage: 1. Mit nahe y = x Linie meine ich einen Abschnitt, der durch y = x und y = x / 2 Linien (y> x / 2) begrenzt ist ). 2. Mit der Linie y = 0 meine ich den Abschnitt, der durch die Linien y = 0 und y = x / 2 begrenzt ist (y <x / 2). Wenn wir alle oben genannten Punkte nehmen und Math.floor entfernen und die Hauptformel vereinfachen, erhalten wir die folgende Formel: Wenn (y> x / 2), dann {return (x + y) / 3} else {return x / 2}
simon
1
@ Simon großartig, das macht es klarer ... ty für Ihre Zeit :)
MarBVI
1
Nur für den Fall, dass der BAPC2017-Wettbewerb auch eine Frage namens Knight's Marathon auf einem unendlichen Brett hatte, die diese Formel perfekt löst. 2017.bapc.eu/files/preliminaries_problems.pdf
Amir-Mousavi
19

Ja, Dijkstra und BFS werden Ihnen die Antwort geben, aber ich denke, der Schachkontext dieses Problems liefert Wissen, das eine Lösung liefern kann, die viel schneller ist als ein generischer Algorithmus für kürzeste Wege, insbesondere auf einem unendlichen Schachbrett.

Beschreiben wir der Einfachheit halber das Schachbrett als (x, y) -Ebene. Das Ziel besteht darin, den kürzesten Weg von (x0, y0) nach (x1, y1) nur mit den Kandidatenschritten (+ -1, + -2), (+ -2, + -1) und (+ -2) zu finden , + -2), wie in der PS der Frage beschrieben

Hier ist die neue Beobachtung: Zeichnen Sie ein Quadrat mit den Ecken (x-4, y-4), (x-4, y + 4), (x + 4, y-4), (x + 4, y + 4). . Dieses Set (nennen wir es S4) enthält 32 Punkte. Der kürzeste Weg von einem dieser 32 Punkte zu (x, y) erfordert genau zwei Züge .

Der kürzeste Weg von einem der 24 Punkte in der Menge S3 (ähnlich definiert) zu (x, y) erfordert mindestens zwei Züge .

Wenn also | x1-x0 |> 4 oder | y1-y0 |> 4 ist, ist der kürzeste Weg von (x0, y0) nach (x1, y1) genau zwei Züge größer als der kürzeste Weg von (x0, y0) nach S4. Und das letztere Problem kann mit einfacher Iteration schnell gelöst werden.

Sei N = max (| x1-x0 |, | y1-y0 |). Wenn N> = 4 ist, hat der kürzeste Weg von (x0, y0) nach (x1, y1) Ceil (N / 2) -Schritte.

Steve Tjoa
quelle
1
Ist es nur ich, der über diese Antwort verwirrt ist? "Zeichnen Sie ein Quadrat mit den Ecken (x-4, y-4), (x-4, y + 4), (x + 4, y-4), (x + 4, y + 4). Diese Menge (Aufruf es S4) enthält 32 Punkte ". Nein, tut es nicht, es enthält 81, da es ein 9x9-Quadrat ist? Außerdem sei "N = max (| x1-x0 |, | y1-y0 |). Wenn N> = 4, dann hat der kürzeste Weg von (x0, y0) nach (x1, y1) Ceil (N / 2). Schritte." Das ist nicht wahr, nehmen Sie zum Beispiel x0 = 0, y0 = 0, x1 = 4, y1 = 4, der kürzeste Weg ist 4, nicht 2, wie diese Formel vorschlägt.
Satoshi
1
(1) Die Menge bezieht sich nur auf die Punkte an der Grenze des Quadrats. Das hat 32 Punkte / Standorte. (2) Wenn Sie die PS des Posters über zusätzliche Züge berücksichtigen (siehe auch die Kommentare im ursprünglichen Beitrag), beträgt die Mindestanzahl von Zügen zwei.
Steve Tjoa
Danke, das macht jetzt Sinn :)
Satoshi
Was ist, wenn ein Board unendlich ist? In diesem Fall wird nur BFS gut funktionieren
Oleg Abrazhaev
@SteveTjoa Entschuldigung, ich kann nicht verstehen, warum Sie (+ -2, + -2) Zug erwähnt haben, da es für einen Ritter unmöglich ist
Pavel Bely
12

Die obige O (1) -Antwort [ https://stackoverflow.com/a/8778592/4288232 von Mustafa Serdar Şanlı] funktioniert nicht wirklich. (Überprüfen Sie (1,1) oder (3,2) oder (4,4), abgesehen von den offensichtlichen Randfällen von (1,0) oder (2,2)).

Unten ist eine viel hässlichere Lösung (Python), die funktioniert (mit zusätzlichen "Tests"):

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()
Arielr
quelle
10
Beziehen auf Antworten als aboveoder belowfunktioniert nicht wirklich auf SO.
Petr Peller
1
Hier ist meine Version in Python 2/3. Ich habe versucht, die Lösungsfunktion zu vereinfachen, aber es ist nicht einfach! gist.github.com/TimSC/8b9a80033f3a22a708a4b9741931c591
TimSC
9

Was Sie tun müssen, ist, sich die möglichen Bewegungen des Ritters als Grafik vorzustellen, wobei jede Position auf dem Brett ein Knoten ist und die möglichen Bewegungen zu einer anderen Position als Kante. Der dijkstra-Algorithmus ist nicht erforderlich, da jede Kante das gleiche Gewicht oder den gleichen Abstand hat (sie sind alle genauso einfach oder kurz). Sie können einfach eine BFS-Suche von Ihrem Startpunkt aus durchführen, bis Sie die Endposition erreichen.

Bishnu
quelle
1
+!, für dieses spezielle Problem reicht BFS aus.
TiansHUo
3
BFS könnte ausreichen, aber eine einfache BST wird für viele Abfragen explodieren - Sie müssen die von Ihnen besuchten Quadrate zwischenspeichern. Und dann sieht BFS ein bisschen wie Dijkstras Algorithmus aus ...
Charles Stewart
Was wäre der beste Weg, um alle Positionen zu verfolgen, die wir bereits zurückgelegt haben, damit der BFS-Baum nur vorwärts wächst und wenn wir Knoten entdecken, die an einem neuen Punkt verfügbar sind, fügen wir den älteren Knoten nicht erneut hinzu und bleiben stecken eine Endlosschleife!
Nitish Upreti
Ich nehme hier an, dass wir nur unsere letzte Ritterposition speichern können?
Nitish Upreti
7

Lösung aus ersten Prinzipien in Python

Ich habe dieses Problem zum ersten Mal in einem Codility-Test festgestellt. Sie gaben mir 30 Minuten, um es zu lösen - ich brauchte erheblich länger, um zu diesem Ergebnis zu gelangen! Das Problem war: Wie viele Züge braucht ein Ritter, um von 0,0 nach x, y zu gelangen, wobei nur legale Ritterzüge verwendet werden. x und y waren mehr oder weniger unbegrenzt (wir sprechen hier also nicht von einem einfachen 8x8-Schachbrett).

Sie wollten eine O (1) -Lösung. Ich wollte eine Lösung, bei der das Programm das Problem eindeutig löste (dh ich wollte etwas offensichtlich Richtiges als Graemes Muster - Muster haben die Angewohnheit, dort zusammenzubrechen, wo man nicht hinschaut), und ich wollte mich wirklich nicht auf ein verlassen müssen unargierte Formel, wie in Mustafas Lösung

Also, hier ist meine Lösung für das, was es wert ist. Beginnen Sie wie andere, indem Sie feststellen, dass die Lösung symmetrisch zu den Achsen und Diagonalen ist, sodass wir nur nach 0> = y> = x lösen müssen. Zur Vereinfachung der Erklärung (und des Codes) werde ich das Problem umkehren: Der Ritter beginnt bei x, y und strebt 0,0 an.

Nehmen wir an, wir verkleinern das Problem auf die Nähe des Ursprungs. Wir werden zu gegebener Zeit herausfinden, was "Umgebung" eigentlich bedeutet, aber jetzt schreiben wir einfach einige Lösungen in ein Cheatsheet (Ursprung unten links):

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

Wenn also x, y im Raster angegeben sind, können wir einfach die Anzahl der Bewegungen zum Ursprung ablesen.

Wenn wir außerhalb des Gitters angefangen haben, müssen wir uns zurückarbeiten. Wir führen die 'Mittellinie' ein, die durch y = x / 2 dargestellt wird. Jeder Ritter bei x, y auf dieser Linie kann sich mit einer Reihe von 8-Uhr-Zügen (dh (-2, -1) Zügen) zum Cheatsheet zurückarbeiten. Wenn x, y über der Mittellinie liegt, benötigen wir eine Folge von 8 Uhr und 7 Uhr, und wenn es unter der Mittellinie liegt, benötigen wir eine Folge von 8 Uhr und 10 Uhr Die Uhr bewegt sich. Zwei Dinge, die hier zu beachten sind:

  • Diese Sequenzen sind nachweislich kürzeste Wege. (Soll ich es beweisen, oder ist es offensichtlich?)
  • Wir kümmern uns nur um die Anzahl solcher Züge. Wir können die Züge in beliebiger Reihenfolge kombinieren.

Schauen wir uns also die Bewegungen über der Mittellinie an. Was wir behaupten ist, dass:

  • (dx; dy) = (2,1; 1,2) (n8; n7) (Matrixnotation ohne mathematischen Satz - Spaltenvektor (dx; dy) entspricht der quadratischen Matrix multipliziert mit dem Spaltenvektor (n8; n7) - der Anzahl der 8-Uhr-Züge und Anzahl der 7-Uhr-Züge) und ähnlich;

  • (dx; dy) = (2,2; 1, -1) (n8; n10)

Ich behaupte, dass dx, dy ungefähr (x, y) sein wird, also (x-dx, y-dy) in der Nähe des Ursprungs (was auch immer sich als "Umgebung" herausstellt).

Die beiden Zeilen im Code, die diese Begriffe berechnen, sind die Lösung für diese, aber sie wurden ausgewählt, um einige nützliche Eigenschaften zu haben:

  • Die Formel über der Mittellinie verschiebt (x, y) zu einem von (0,0), (1,1) oder (2,2).
  • Die Formel unter der Mittellinie verschiebt (x, y) zu einem von (0,0), (1,0), (2,0) oder (1,1).

(Möchten Sie Beweise dafür?) Die Entfernung des Ritters ist also die Summe aus n7, n8, n10 und Cheatsheet [x-dx, y-dy], und unser Cheatsheet reduziert sich auf Folgendes:

. . 4
. 2 .
0 3 2

Dies ist noch nicht das Ende der Geschichte. Schauen Sie sich die 3 in der unteren Reihe an. Die einzigen Möglichkeiten, wie wir dies erreichen können, sind entweder:

  • Wir haben dort angefangen oder
  • Wir zogen dorthin, in einer Folge von 8 Uhr und 10 Uhr. Aber wenn der letzte Zug eine 8 Uhr war (was berechtigt ist, da wir unsere Züge in beliebiger Reihenfolge ausführen können), müssen wir (3,1) durchlaufen haben, dessen Abstand tatsächlich 2 beträgt (wie Sie können) siehe aus dem Original-Cheatsheet). Wir sollten also einen Zug um 8 Uhr zurückverfolgen und insgesamt zwei Züge sparen.

Es gibt eine ähnliche Optimierung mit der 4 oben rechts. Abgesehen davon, dass Sie dort beginnen, können Sie dies nur mit einer 8-Uhr-Bewegung von (4,3) erreichen. Das steht nicht auf dem Cheatsheet, aber wenn es dort wäre, wäre seine Entfernung 3, weil wir stattdessen 7 Uhr auf (3,1) hätten haben können, was eine Entfernung von nur 2 hat. Also sollten wir eine zurückverfolgen Bewegen Sie sich um 8 Uhr und gehen Sie dann um 7 Uhr vorwärts.

Also müssen wir dem Cheatsheet noch eine Nummer hinzufügen:

. . 4
. 2 . 2
0 3 2

(Hinweis: Es gibt eine ganze Reihe von Back-Tracking-Optimierungen von (0,1) und (0,2), aber da der Löser uns niemals dorthin bringt, müssen wir uns keine Sorgen machen.)

Hier ist also ein Python-Code, um dies zu bewerten:

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]

Wenn Sie übrigens eine tatsächliche Route kennen möchten, bietet dieser Algorithmus auch Folgendes: Es handelt sich lediglich um eine Folge von n7 7-Uhr-Bewegungen, gefolgt von (oder durchsetzt mit) n8 8-Uhr-Bewegungen, n10 10- Die Uhr bewegt sich und welcher Tanz auch immer vom Cheatsheet diktiert wird (der sich selbst in einem Cheatsheet befinden kann).

Nun: Wie man das beweist, ist richtig. Es reicht nicht aus, diese Ergebnisse nur mit einer Tabelle mit richtigen Antworten zu vergleichen, da das Problem selbst unbegrenzt ist. Aber wir können sagen, wenn der Abstand des Ritters von einem Quadrat s d ist, dann muss der Abstand des Ritters von (s + m) entweder d-1 oder d + 1 sein, wenn {m} die Menge der legalen Bewegungen von s ist für alle m. (Benötigen Sie einen Beweis dafür?) Außerdem muss es mindestens ein solches Quadrat geben, dessen Abstand d-1 ist, es sei denn, s ist der Ursprung. Wir können also die Richtigkeit beweisen, indem wir zeigen, dass diese Eigenschaft für jedes Quadrat gilt. So:

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

Alternativ können wir die Richtigkeit eines beliebigen Quadrats beweisen, indem wir die Route von s bergab zum Ursprung verfolgen. Überprüfen Sie zuerst s auf Angemessenheit wie oben und wählen Sie dann s + m so aus, dass der Abstand (s + m) == d-1 ist. Wiederholen, bis wir den Ursprung erreichen.

Howzat?

Jules May
quelle
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.

Ich habe Graphen noch nicht studiert. Aufgrund des Problems, sie durch einfache Arrays zu implementieren, konnte ich keine andere Lösung als diese ableiten. Ich behandelte die Positionen nicht als Ränge und Dateien (die übliche Schachnotation), sondern als Array-Indizes. Zu Ihrer Information, dies ist nur für ein 8 * 8-Schachbrett. Verbesserungsvorschläge sind immer willkommen.

* Die Kommentare sollten für Ihr Verständnis der Logik ausreichen. Sie können jedoch immer fragen.

* Überprüft auf DEV-C ++ 4.9.9.2 Compiler (Bloodshed Software).

jigsawmnc
quelle
2

Ich denke, dass dies auch Ihnen helfen könnte ..

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

und Verwenden der dynamischen Programmierung, um die Lösung zu erhalten.

PS: Es verwendet das BFS, ohne sich die Mühe machen zu müssen, die Knoten und Kanten des Graphen zu deklarieren.

Abhishek
quelle
1

Hier ist eine Lösung für dieses spezielle Problem, das in Perl implementiert ist. Es wird einer der kürzesten Wege angezeigt - in einigen Fällen kann es mehr als einen geben.

Ich habe keinen der oben beschriebenen Algorithmen verwendet - aber es wäre schön, ihn mit anderen Lösungen zu vergleichen.

#!/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__
user3150039
quelle
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));
    }
}
Rahul Kurup
quelle
0

Nur Ruby-Code aus Graeme Pyles Antwort-Jsfiddle oben , der den gesamten zusätzlichen Code gestreift und in Ruby umgewandelt wurde, um eine Lösung durch seinen Algorithmus zu erhalten, scheint zu funktionieren. Trotzdem testen:

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)

Die einzige Absicht ist es, jemandem Zeit beim Konvertieren von Code zu sparen, wenn jemand vollständigen Code benötigt.

Zia Ul Rehman Mughal
quelle
0

Hier ist die PHP-Version von Jules Mays Funktion

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];
}
Mircea Soaica
quelle
0

Hier ist mein Programm. Dies ist keine perfekte Lösung. In der Rekursionsfunktion müssen viele Änderungen vorgenommen werden. Aber dieses Endergebnis ist perfekt. Ich habe versucht, ein bisschen zu optimieren.

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;
    }

}
Arun
quelle
1
Es kann weiter optimiert werden, um Duplikate zu vermeiden.
Arun
-1

Hier ist eine C-Version, die auf dem Code von Mustafa Serdar Şanlı basiert und für ein Finit Board funktioniert:

#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));
    }
}

Testen Sie es hier mit einem Beweis gegen eine rekursive Lösung

Johan du Toit
quelle
1
Das Testen einer endlichen Anzahl von Fällen ist kein Beweis.
BlenderBender