Il percorso più breve del cavaliere sulla scacchiera


95

Mi sono allenato per un imminente concorso di programmazione e mi sono imbattuto in una domanda di cui sono completamente sconcertato. Tuttavia, mi sento come se fosse un concetto che dovrei imparare ora piuttosto che incrociare le dita che non viene mai fuori.

Fondamentalmente, si tratta di un pezzo da cavaliere su una scacchiera. Vengono forniti due input: posizione iniziale e posizione finale. L'obiettivo è quindi calcolare e stampare il percorso più breve che il cavaliere può intraprendere per raggiungere la posizione di destinazione.

Non ho mai avuto a che fare con cose del percorso più breve e non so nemmeno da dove cominciare. Quale logica utilizzo per affrontare questo problema?

PS Se è di qualche importanza, vogliono che tu integri le normali mosse del cavaliere permettendogli anche di spostarsi ai quattro angoli della casella formata dalle (potenzialmente) otto mosse che un cavaliere può fare, dato che il centro della casella è la posizione del cavaliere.


Potresti chiarire la PS? Vuoi dire, se un cavaliere è in E4, può spostarsi in C2, C6, G2 e G6?
Steve Tjoa

Sì, oltre alle sue normali mosse.
Kyle Hughes,

1
Ecco alcune analisi matematiche del problema: math.stackexchange.com/questions/104700/…
Graeme Pyle

Risposte:


28

Hai un grafico qui, dove tutte le mosse disponibili sono collegate (valore = 1) e le mosse non disponibili sono disconnesse (valore = 0), la matrice sparsa sarebbe come:

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

E il percorso più breve di due punti in un grafico può essere trovato utilizzando http://en.wikipedia.org/wiki/Dijkstra's_algorithm

Pseudo-codice dalla pagina 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[]

MODIFICARE:

  1. come idiota, ha detto che usando il http://en.wikipedia.org/wiki/A*_algorithm può essere più veloce.
  2. il modo più veloce, è pre-calcolare tutte le distanze e salvarle in una matrice piena 8x8. beh, lo chiamerei barare e funziona solo perché il problema è piccolo. Ma a volte le competizioni controllano la velocità di esecuzione del tuo programma.
  3. Il punto principale è che se ti stai preparando per una competizione di programmazione, devi conoscere algoritmi comuni, incluso quello di Dijkstra. Un buon punto di partenza è leggere Introduction to AlgorithmsISBN 0-262-03384-4. Oppure potresti provare wikipedia, http://en.wikipedia.org/wiki/List_of_algorithms

Questo sembra essere complesso rispetto alla soluzione di Mustafa di seguito.
lpapp

Cosa intendi per mossa non disponibile? Un cavaliere può raggiungere qualsiasi casella giusto !?
everlasto

51

EDIT: Vedi la risposta di simon , dove ha corretto la formula presentata qui.

In realtà esiste una formula O (1)

Questa è l'immagine che ho fatto di visualizzarla (Piazze un cavaliere può raggiungere il N ° movimento sono verniciate con lo stesso colore). La mossa del cavaliere

Riesci a notare lo schema qui?

Sebbene possiamo vedere lo schema, è davvero difficile trovare la funzione f( x , y )che restituisce il numero di mosse necessarie per andare da un quadrato ( 0 , 0 )all'altro( x , y )

Ma ecco la formula che funziona quando 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 );
}

Nota: questa domanda è stata posta il giorno 1 di SACO 2007
e le soluzioni sono qui


8
C'è qualche possibilità che potresti descrivere come hai elaborato quella formula?
kybernetikos

3
Questo codice funziona? Se un cavaliere è in posizione (0,0) e voglio spostarlo al punto (1,0). Ciò soddisfa 0 <= y <= x. delta = 1-0 = 1. y non è maggiore di delta (0 <1). Ciò significa che vado per l'altro caso. delta - 2 * ((delta - y) / 4) = 1-2 ((1-0) / 4) = 1-1 / 2 = 1. Non posso muovere il cavaliere da (0,0) a (1,0) in una sola mossa. La domanda è: questo algoritmo funziona? O cosa sto facendo di sbagliato?
SimpleApp

3
Sembra che funzioni solo per posizioni possibili dirette. Ma se l'utente fornisce (2,2) restituisce 0 e se l'utente fornisce (4,4) restituisce 2 che sono errati.
yunas

6
Dovrebbe essere 2 * floor((y - delta) / 3) + deltae delta - 2 * floor((delta - y) / 4). Questa è la soluzione ufficiale da questa pagina del concorso, ma è sbagliata. Questa prima equazione (da if) restituisce risposte sbagliate. Sulla scacchiera [-1000..1000] x [-1000..1000], che è grande 2001x2001 (ma logicamente illimitata), la risposta data conta 2.669.329 di 4.004.001 campi corretti (66,66%). Qualcuno conosce la soluzione di lavoro senza alcun loop?
Robo Robok

2
Sono d'accordo che questa soluzione non funziona. Vedi altre risposte come stackoverflow.com/a/26888893/4288232 per una soluzione O (1) funzionante.
TimSC

45

Ecco una soluzione O (1) corretta, ma per il caso in cui il cavaliere si muove solo come un cavaliere degli scacchi e su una scacchiera infinita:

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

La chiave per trovarlo è notare gli schemi che emergono quando disegni il tabellone. Nel diagramma sottostante, il numero nel quadrato è il numero minimo di mosse necessarie per raggiungere quel quadrato (puoi usare la ricerca in ampiezza per trovarlo):

Modelli

Poiché la soluzione è simmetrica rispetto agli assi e alle diagonali, ho disegnato solo il caso x> = 0 e y> = x.

Il blocco in basso a sinistra è la posizione di partenza e i numeri nei blocchi rappresentano il numero minimo di mosse per raggiungere quei blocchi.

Ci sono 3 modelli da notare:

  • I gruppi verticali blu crescenti di 4
  • Le diagonali rosse "primarie" (vanno dall'alto a sinistra verso il basso a destra, come una barra rovesciata)
  • Le diagonali verdi "secondarie" (stesso orientamento del rosso)

(Assicurati di vedere entrambi i gruppi di diagonali come dall'alto a sinistra verso il basso a destra. Hanno un conteggio delle mosse costante. Le diagonali in basso a sinistra in alto a destra sono molto più complesse).

Puoi derivare formule per ciascuno. I blocchi gialli sono casi speciali. Quindi la soluzione diventa:

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

}

con il più difficile essendo i gruppi verticali:

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

}

Vedi il violino per gli altri casi.

Forse ci sono modelli più semplici o più eleganti che mi sono perso? Se è così, mi piacerebbe vederli. In particolare, noto dei motivi diagonali nelle custodie verticali blu, ma non li ho esplorati. Indipendentemente da ciò, questa soluzione soddisfa ancora il vincolo O (1).


Questo non sembra gestire i casi d'angolo (letterali). Se lo "0" è la casella in basso a sinistra sul tabellone (a1), non puoi raggiungere lo spazio "2" più vicino (b2) in due mosse. Perché per farlo la tua prima mossa dovrebbe essere nello spazio inesistente a sinistra di (a3).
John Hascall

Bene, ho cambiato la mia risposta per includere l'ipotesi della scacchiera infinita
Graeme Pyle

@JonatasWalker Per favore, spiega, non vedo un problema che va da (8,0) a (0,0). Ci vogliono 4 mosse?
Graeme Pyle

Scusa @GraemePyle, colpa mia, rimuovendo il mio commento.
Jonatas Walker

2
ciao @GraemePyle - Sono d'accordo con te che questo è il miglior approccio di programmazione complessivo. Ottimo diagramma a proposito!
Fattie

22

Problema molto interessante che ho riscontrato di recente. Dopo aver guardato alcune soluzioni sono stato tentato di recuperare la formula analitica ( O(1) time and space complexity) indicato sulla SACO 2007 Day 1 soluzioni .

Prima di tutto voglio apprezzare Graeme Pyle per la visualizzazione molto bella che mi ha aiutato a correggere la formula.

Per qualche motivo (magari per semplificazione o bellezza o solo un errore) hanno spostato il minussegno in flooroperatore, di conseguenza hanno sbagliato la formula floor(-a) != -floor(a) for any a.

Ecco la formula analitica corretta:

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 formula funziona per tutte le coppie (x, y) (dopo aver applicato gli assi e la simmetria diagonale) tranne (1,0) e (2,2) i casi d'angolo, che non sono soddisfacenti per il modello e codificati nel seguente frammento:

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>

Nota: jQuery utilizzato solo per l'illustrazione, per il codice vedere la distancefunzione.


2
@OlegAbrazhaev La funzione "distanza" è una funzione analitica e può calcolare il numero di passi per una data posizione (x, y) in tempo O (1). Fondamentalmente in questa formula non dipendiamo dalla scheda (immagino che per "tavola infinita" intendi quella proprietà), quindi funzionerà.
simon

2
@simon Qualcuno può spiegare la formula principale per favore? Trovo difficile essere in grado di spiegarlo con parole semplici
MarBVI

1
@MarBVI Se ci avviciniamo alla linea y = x possiamo diminuire x + y di 3 in ogni movimento rimanendo vicino alla linea y = x. Se ci avviciniamo alla linea y = 0, possiamo diminuire x di 2 in ogni movimento rimanendo vicino alla linea y = 0. Questo è il motivo per cui abbiamo 2 casi, più precisamente qui quello che intendo dire vicino a una particolare linea: 1. Per vicino a y = x linea intendo sezione limitata da y = x e y = x / 2 linee (y> x / 2 ). 2. Con vicino y = 0 linea intendo la sezione limitata da y = 0 e y = x / 2 linee (y <x / 2). Prendendo tutto ciò sopra menzionato e se rimuoviamo Math.floor e semplifichiamo la formula principale, otterremo la seguente formula: if (y> x / 2) then {return (x + y) / 3} else {return x / 2}
simon

1
@simon fantastico, questo lo rende più chiaro ... per il tuo tempo :)
MarBVI

1
per ogni evenienza, il concorso BAPC2017 aveva anche una domanda chiamata Knight's Marathon su una tavola infinita che questa formula risolve perfettamente. 2017.bapc.eu/files/preliminaries_problems.pdf
Amir-Mousavi

19

Sì, Dijkstra e BFS ti daranno la risposta, ma penso che il contesto scacchistico di questo problema fornisca la conoscenza che può produrre una soluzione che è molto più veloce di un generico algoritmo del percorso più breve, specialmente su una scacchiera infinita.

Per semplicità, descriviamo la scacchiera come il piano (x, y). L'obiettivo è trovare il percorso più breve da (x0, y0) a (x1, y1) utilizzando solo i passaggi candidati (+ -1, + -2), (+ -2, + -1) e (+ -2 , + -2), come descritto nel PS della domanda

Ecco la nuova osservazione: disegna un quadrato con angoli (x-4, y-4), (x-4, y + 4), (x + 4, y-4), (x + 4, y + 4) . Questo set (chiamalo S4) contiene 32 punti. Il percorso più breve da uno qualsiasi di questi 32 punti a (x, y) richiede esattamente due mosse .

Il percorso più breve da uno qualsiasi dei 24 punti nell'insieme S3 (definito in modo simile) a (x, y) richiede almeno due mosse .

Pertanto, se | x1-x0 |> 4 o | y1-y0 |> 4, il percorso più breve da (x0, y0) a (x1, y1) è esattamente due mosse maggiore del percorso più breve da (x0, y0) a S4. E quest'ultimo problema può essere risolto rapidamente con una semplice iterazione.

Sia N = max (| x1-x0 |, | y1-y0 |). Se N> = 4, il percorso più breve da (x0, y0) a (x1, y1) ha passi ceil (N / 2) .


1
Sono solo io a essere confuso su questa risposta? "disegna un quadrato con angoli (x-4, y-4), (x-4, y + 4), (x + 4, y-4), (x + 4, y + 4). Questo insieme (chiama it S4) contiene 32 punti ". No, non contiene 81 in quanto è un quadrato 9x9? Inoltre, "Sia N = max (| x1-x0 |, | y1-y0 |). Se N> = 4, il percorso più breve da (x0, y0) a (x1, y1) ha ceil (N / 2) passi. " Non è vero, prendi ad esempio x0 = 0, y0 = 0, x1 = 4, y1 = 4, il percorso più breve è 4, non 2 come suggerisce la formula.
satoshi

1
(1) Il set si riferisce solo ai punti sul confine del quadrato stesso. Questo ha 32 punti / posizioni. (2) Quando si prende in considerazione il PS del poster sulle mosse supplementari (vedere anche i commenti nel post originale), il numero minimo di mosse diventa due.
Steve Tjoa

Grazie, ora ha senso :)
satoshi

cosa succede se una tavola è infinita? in questo caso solo BFS funzionerà bene
Oleg Abrazhaev

@SteveTjoa scusa, non riesco a capire perché hai menzionato (+ -2, + -2) mossa, poiché è impossibile per un cavaliere
Pavel Bely,

12

La risposta O (1) sopra [ https://stackoverflow.com/a/8778592/4288232 di Mustafa Serdar Şanlı] non funziona davvero. (Controllare (1,1) o (3,2) o (4,4), a parte gli ovvi casi limite di (1,0) o (2,2)).

Di seguito è una soluzione molto più brutta (python), che funziona (con l'aggiunta di "test"):

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
Fare riferimento alle risposte come aboveo belownon funziona davvero su SO.
Petr Peller

1
Ecco la mia versione in python 2/3. Ho provato a semplificare la funzione di risoluzione ma non è facile! gist.github.com/TimSC/8b9a80033f3a22a708a4b9741931c591
TimSC

9

Quello che devi fare è pensare alle possibili mosse del cavaliere come un grafico, dove ogni posizione sulla scacchiera è un nodo e le possibili mosse in un'altra posizione come un bordo. Non è necessario l'algoritmo di dijkstra, perché ogni bordo ha lo stesso peso o distanza (sono tutti altrettanto facili o corti da fare). Puoi semplicemente eseguire una ricerca BFS dal punto di partenza fino a raggiungere la posizione finale.


1
+ !, per questo problema specifico è sufficiente BFS.
TiansHUo

3
BFS potrebbe essere sufficiente, ma un semplice BST esploderà per molte query: dovrai memorizzare nella cache i quadrati che hai visitato. E poi BFS inizia ad assomigliare un po 'all'algoritmo di Dijkstra ...
Charles Stewart

Quale sarebbe il modo migliore per tenere traccia di tutta la posizione che abbiamo già percorso in modo che l'albero BFS cresca solo in avanti e quando scopriamo nodi disponibili da un nuovo punto, non finiamo per aggiungere di nuovo il nodo più vecchio .. e rimanere bloccati in un ciclo infinito!
Nitish Upreti

Suppongo che qui possiamo semplicemente memorizzare la nostra ultima posizione di cavaliere?
Nitish Upreti

7

Soluzione dai primi principi in Python

Ho riscontrato questo problema per la prima volta in un test di Codility. Mi hanno dato 30 minuti per risolverlo - mi ci è voluto molto più tempo per arrivare a questo risultato! Il problema era: quante mosse impiega un cavaliere per passare da 0,0 ax, y usando solo le mosse legali del cavaliere. xey erano più o meno illimitate (quindi non stiamo parlando di una semplice scacchiera 8x8).

Volevano una soluzione O (1). Volevo una soluzione in cui il programma risolvesse chiaramente il problema (cioè volevo qualcosa di più evidentemente giusto del modello di Graeme - i modelli hanno l'abitudine di rompersi dove non stai guardando), e volevo davvero non dover fare affidamento su un formula non valutata, come nella soluzione di Mustafa

Quindi, ecco la mia soluzione, per quello che vale. Inizia, come altri, notando che la soluzione è simmetrica rispetto agli assi e alle diagonali, quindi dobbiamo risolvere solo per 0> = y> = x. Per semplicità di spiegazione (e codice) ribalterò il problema: il cavaliere parte da x, y, e punta a 0,0.

Supponiamo di ridurre il problema in prossimità dell'origine. A tempo debito arriveremo a cosa significa effettivamente `` vittoria '', ma per ora, scriviamo solo alcune soluzioni in un cheatsheet (origine in basso a sinistra):

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

Quindi, dati x, y sulla griglia, possiamo semplicemente leggere il numero di mosse all'origine.

Se siamo partiti fuori dalla griglia, dobbiamo tornare indietro. Introduciamo la 'linea mediana', che è la linea rappresentata da y = x / 2. Qualsiasi cavaliere in x, y su quella linea può tornare al cheatsheet usando una serie di mosse a ore 8 (ovvero: (-2, -1) mosse). Se x, y si trova sopra la linea mediana, avremo bisogno di una successione di mosse a ore 8 e ore 7, e se si trova al di sotto della linea mediana, avremo bisogno di una successione di ore 8 e ore 10 'l'orologio si muove. Due cose da notare qui:

  • Queste sequenze sono probabilmente i percorsi più brevi. (Vuoi che lo dimostri, o è ovvio?)
  • Ci interessa solo il numero di tali mosse. Possiamo mescolare e abbinare le mosse in qualsiasi ordine.

Quindi, diamo un'occhiata alle mosse sopra la linea mediana. Quello che stiamo affermando è che:

  • (dx; dy) = (2,1; 1,2) (n8; n7) (notazione matriciale, senza impaginazione matematica - il vettore colonna (dx; dy) è uguale alla matrice quadrata moltiplicata per il vettore colonna (n8; n7) - il numero di mosse a ore 8 e numero di mosse a ore 7), e similmente;

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

Sto affermando che dx, dy sarà all'incirca (x, y), quindi (x-dx, y-dy) sarà nelle vicinanze dell'origine (qualunque sia la "vicinanza" che risulta essere).

Le due righe nel codice che calcolano questi termini sono la soluzione a questi, ma sono selezionate per avere alcune proprietà utili:

  • La formula sopra la linea mediana si sposta (x, y) su uno tra (0,0), (1,1) o (2,2).
  • La formula sotto la linea mediana si sposta (x, y) su uno tra (0,0), (1,0), (2,0) o (1,1).

(Vorresti delle prove di questi?) Quindi, la distanza del Cavaliere sarà la somma di n7, n8, n10 e cheatsheet [x-dx, y-dy], e il nostro cheatsheet si riduce a questo:

. . 4
. 2 .
0 3 2

Ora, questa non è proprio la fine della storia. Guarda il 3 nella riga inferiore. Gli unici modi in cui possiamo raggiungere questo obiettivo sono:

  • Abbiamo iniziato da lì, o
  • Ci siamo trasferiti lì, con una sequenza di movimenti delle 8 e delle 10. Ma se l'ultima mossa era un 8 (che ha il diritto di essere, dato che possiamo fare le nostre mosse in qualsiasi ordine), allora dobbiamo essere passati attraverso (3,1), la cui distanza è effettivamente 2 (come puoi vedi dal cheatsheet originale). Quindi quello che dovremmo fare è tornare indietro di una mossa alle 8, salvando due mosse in totale.

C'è un'ottimizzazione simile da avere con il 4 in alto a destra. Oltre a partire da lì, l'unico modo per raggiungerlo è spostarsi a ore 8 dalla (4,3). Non è nel cheatsheet, ma se fosse lì, la sua distanza sarebbe 3, perché invece potremmo avere 7 in punto a (3,1), che ha una distanza di solo 2. Quindi, dovremmo tornare indietro di uno Spostarsi a ore 8, quindi andare avanti all'una delle 7.

Quindi, dobbiamo aggiungere un altro numero al cheatsheet:

. . 4
. 2 . 2
0 3 2

(Nota: ci sono un intero carico di ottimizzazioni di back-tracking da (0,1) e (0,2) ma poiché il risolutore non ci porterà mai lì, non dobbiamo preoccuparci di loro.)

Quindi ecco, quindi, del codice Python per valutare questo:

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]

Per inciso, se vuoi conoscere un percorso effettivo, questo algoritmo lo fornisce anche questo: è semplicemente una successione di n7 movimenti a ore 7, seguito da (o intervallato da) n8 movimenti a ore 8, n10 10- o'clock si muove e qualunque danza sia dettata dal cheatsheet (che, a sua volta, può essere in un cheatsheet).

Ora: come dimostrare che è giusto. Non è sufficiente confrontare questi risultati con una tabella di risposte corrette, perché il problema stesso è illimitato. Ma possiamo dire che, se la distanza del Cavaliere di un quadrato s è d, allora se {m} è l'insieme delle mosse legali da s, la distanza del Cavaliere di (s + m) deve essere d-1 o d + 1 per tutti m. (Hai bisogno di una prova di ciò?) Inoltre, deve esserci almeno un quadrato di questo tipo la cui distanza sia d-1, a meno che s non sia l'origine. Quindi, possiamo dimostrare la correttezza mostrando che questa proprietà vale per ogni quadrato. Quindi:

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

In alternativa, possiamo provare la correttezza di uno qualsiasi dei quadrati inseguendo il percorso da s in discesa fino all'origine. Per prima cosa, controlla la ragionevolezza di s come sopra, quindi seleziona qualsiasi s + m tale che la distanza (s + m) == d-1. Ripeti fino a raggiungere 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.

Non ho ancora studiato i grafici..per quanto riguarda il problema di implementarlo tramite semplici array, non ho potuto ricavare alcuna soluzione diversa da questa. Ho trattato le posizioni non come file e file (la solita notazione scacchistica), ma come indici di matrice. Cordiali saluti, questo è solo per una scacchiera 8 * 8. Qualsiasi consiglio di miglioramento è sempre ben accetto.

* I commenti dovrebbero essere sufficienti per la tua comprensione della logica. Tuttavia, puoi sempre chiedere.

* Controllato sul compilatore DEV-C ++ 4.9.9.2 (Bloodshed Software).


2

Penso che questo potrebbe anche aiutarti ..

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

e utilizzando la programmazione dinamica per ottenere la soluzione.

PS: usa il BFS senza doversi prendere la briga di dichiarare i nodi e gli spigoli del grafico.


1

Ecco una soluzione per questo particolare problema implementata in Perl. Mostrerà uno dei percorsi più brevi, in alcuni casi potrebbe essercene più di uno.

Non ho utilizzato nessuno degli algoritmi sopra descritti, ma sarebbe bello confrontarlo con altre soluzioni.

#!/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

Solo il codice ruby ​​dal jsfiddle della risposta di Graeme Pyle sopra , lo striping di tutto il codice extra e convertito il rimanente in ruby ​​solo per ottenere la soluzione dal suo algoritmo, sembra funzionare. Tuttavia, sto ancora testando:

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)

L'unica intenzione è quella di risparmiare un po 'di tempo a qualcuno nella conversione del codice se qualcuno ha bisogno del codice completo.


0

ecco la versione PHP della funzione di 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

Ecco il mio programma. Questa non è una soluzione perfetta. Ci sono molte modifiche da fare nella funzione di ricorsione. Ma questo risultato finale è perfetto. Ho provato a ottimizzare un po '.

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
Può essere ulteriormente ottimizzato per evitare duplicati.
Arun

-1

Ecco una versione C basata sul codice Mustafa Serdar Şanlı che funziona per una scheda finita:

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

Provalo qui con la prova contro una soluzione ricorsiva


1
Testare un numero finito di casi non è una prova.
BlenderBender
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.