Percorso ottimale attraverso una matrice


19

Data una matrice composta da numeri interi positivi, genera il percorso con la somma più bassa quando attraversi l'elemento in alto a sinistra in basso a destra. Puoi muoverti in verticale, in orizzontale e in diagonale. Si noti che è possibile spostare sia su / giù, destra / sinistra e in diagonale su tutti i lati.

Esempio:

 1*   9    7    3   10    2    2
10    4*   1*   1*   1*   7    8
 3    6    3    8    9    5*   7
 8   10    2    5    2    1*   4
 5    1    1    3    6    7    9*

Il percorso che dà la somma più bassa è contrassegnato da asterischi e risulta nella seguente somma: 1 + 4 + 1 + 1 + 1 + 5 + 1 + 9 = 23 .

Casi test:

1   1   1
1   1   1
Output: 3

 7    9    6    6    4
 6    5    9    1    6
10    7   10    4    3
 4    2    2    3    7
 9    2    7    9    4
Output: 28

2  42   6   4   1
3  33   1   1   1
4  21   7  59   1
1   7   6  49   1
1   9   2  39   1
Output: 27 (2+3+4+7+7+1+1+1+1)

 5    6    7    4    4
12   12   25   25   25
 9    4   25    9    5
 7    4   25    1   12
 4    4    4    4    4
Output: 34 (5+12+4+4+4+1+4)

1   1   1   1
9   9   9   1
1   9   9   9
1   9   9   9
1   1   1   1
Output: 15

 2   55    5    3    1    1    4    1
 2   56    1   99   99   99   99    5
 3   57    5    2    2    2   99    1
 3   58    4    2    8    1   99    2
 4   65   66   67   68    3   99    3
 2    5    4    3    3    4   99    5
75   76   77   78   79   80   81    2
 5    4    5    1    1    3    3    2
Output: 67 (2+2+3+3+4+5+4+3+3+3+1+2+2+1+3+1+1+4+5+1+2+3+5+2+2)

Questo è quindi vince il codice più corto in ogni lingua.


Molto simile , sebbene non consenta il movimento diagonale.
Mego

7
@WheatWizard Non sono d'accordo. A parte le differenze per lo più superficiali che questa sfida consente al movimento diagonale e tutte le posizioni sono raggiungibili, l'altra sfida richiede il ritorno del percorso stesso piuttosto che solo il costo del percorso. A meno che non si utilizzino componenti integrati che restituiscono entrambi, il codice non è intercambiabile.
becher

@beaker Non vedo davvero la differenza. Devi trovare il percorso per conoscerne la lunghezza. La differenza qui è una differenza piuttosto piccola nell'output secondo me e questa sfida non offre nulla di nuovo o interessante non già coperto da quella sfida.
Mago del grano

1
@WheatWizard La mia soluzione qui non trova il percorso. Si potrebbe trovare il percorso, ma non senza una matrice predecessore separata e la logica per evitare di fare un nodo suo predecessore. Per non parlare del recupero del percorso alla fine.
becher

@beaker La modifica è piuttosto banale secondo me. Indipendentemente dal fatto che la questione dei duplicati non è se ogni singola voce valida su una sfida possa essere trasferita con il minimo sforzo, si tratta del caso generale. Non solo penso che la maggior parte degli sforzi qui possano essere portati, ma non credo che questa sfida offra qualcosa di nuovo o di interessante dall'altro.
Mago del grano

Risposte:


8

JavaScript, 442 412 408 358 byte

Questa è la mia prima presentazione PPCG. Il feedback sarebbe apprezzato.

(m,h=m.length,w=m[0].length)=>{for(i=0;i<h*w;i++)for(x=0;x<w;x++){for(y=0;y<h;y++){if(m[y][x]%1==0)m[y][x]={c:m[y][x],t:m[y][x]};for(X=-1;X<=1;X++)for(Y=-1;Y<=1;Y++){t=x+X;v=y+Y;if((X==0&&Y==0)||t<0||t>=w||v<0||v>=h)continue;if(m[v][t]%1==0)m[v][t]={c:m[v][t],t:null};c=m[y][x].t+m[v][t].c;if (c<m[v][t].t||m[v][t].t==null)m[v][t].t=c}}}return m[h-1][w-1].t}

Questo richiede un array multidimensionale come input.

Spiegazione

Fondamentalmente, attraversa ripetutamente tutte le celle regolando il costo più basso noto per raggiungere ciascuno dei vicini. Alla fine, la griglia raggiungerà uno stato in cui il costo totale per raggiungere in basso a destra è il costo più basso per arrivarci.

dimostrazione

f=(m,h=m.length,w=m[0].length)=>{for(i=0;i<h*w;i++)for(x=0;x<w;x++){for(y=0;y<h;y++){if(m[y][x]%1==0)m[y][x]={c:m[y][x],t:m[y][x]};for(X=-1;X<=1;X++)for(Y=-1;Y<=1;Y++){t=x+X;v=y+Y;if((X==0&&Y==0)||t<0||t>=w||v<0||v>=h)continue;if(m[v][t]%1==0)m[v][t]={c:m[v][t],t:null};c=m[y][x].t+m[v][t].c;if (c<m[v][t].t||m[v][t].t==null)m[v][t].t=c}}}return m[h-1][w-1].t}

//Tests
console.log(f([[1,1,1],[1,1,1]])===3);
console.log(f([[7,9,6,6,4],[6,5,9,1,6],[10,7,10,4,3],[4,2,2,3,7],[9,2,7,9,4]])===28);
console.log(f([[2,42,6,4,1],[3,33,1,1,1],[4,21,7,59,1],[1,7,6,49,1],[1,9,2,39,1]])===27);
console.log(f([[5,6,7,4,4],[12,12,25,25,25],[9,4,25,9,5],[7,4,25,1,12],[4,4,4,4,4]])===34); 
console.log(f([[1,1,1,1],[9,9,9,1],[1,9,9,9],[1,9,9,9],[1,1,1,1]])===15)
console.log(f([[2,55,5,3,1,1,4,1],[2,56,1,99,99,99,99,5],[3,57,5,2,2,2,99,1],[3,58,4,2,8,1,99,2],[4,65,66,67,68,3,99,3],[2,5,4,3,3,4,99,5],[75,76,77,78,79,80,81,2],[5,4,5,1,1,3,3,2]])===67);

Modifica: ringraziamenti speciali a @ETHproductions per avermi aiutato a radere dozzine di byte gustosi.

Ancora grazie a @Stewie Griffin per i tuoi suggerimenti che hanno eliminato 50 byte.


3
Benvenuti in PPCG! Ci sono alcuni spazi extra che potresti rimuovere verso la fine (conto 5 in totale) e non hai bisogno di alcun punto e virgola direttamente prima di un }che dovrebbe salvare qualche byte. Inoltre, non è necessario dichiarare le variabili; Penso che rimuovere la vars dovrebbe farti risparmiare altri 24 byte in totale.
ETHproductions

2
Benvenuto in PPCG =) Sono contento che tu abbia scelto una delle mie sfide come punto di partenza. Il mio unico commento: amo le spiegazioni. È facoltativo, quindi non è necessario aggiungerlo se non lo si desidera. :)
Stewie Griffin il

Penso che forse l'archiviazione m[v][t]come variabile: t=x+X;v=y+Y;k=m[v][t]sarà ancora più breve ...?
Stewie Griffin,

7

Python 3 + numpy + scipy , 239 222 186 byte

from numpy import*
from scipy.sparse.csgraph import*
def f(M):m,n=s=M.shape;x,y=indices(s);return dijkstra([(M*(abs(i//n-x)<2)*(abs(i%n-y)<2)).flatten()for i in range(m*n)])[0,-1]+M[0,0]

Provalo online!


6

Pacchetto di elaborazione immagini + ottava, 175 162 157 151 142 139 byte

Salvato 14 byte grazie a @Luis Mendo e 1 byte grazie a @notjagan

function P(G)A=inf(z=size(G));A(1)=G(1);for k=G(:)'B=im2col(padarray(A,[1,1],inf),[3,3])+G(:)';B(5,:)-=G(:)';A=reshape(min(B),z);end,A(end)

Utilizza il pacchetto di elaborazione delle immagini, perché perché no? Non è così che tutti risolvono i problemi dei grafici?

Provalo online!

esploso

function P(G)
   A=inf(z=size(G));         % Initialize distance array to all Inf
   A(1)=G(1);                % Make A(1) = cost of start cell
   for k=G(:)'               % For a really long time...
      B=im2col(padarray(A,[1,1],inf),[3,3])+G(:)';
       %  B=padarray(A,[1,1],inf);     % Add border of Inf around distance array
       %  B=im2col(B,[3,3]);           % Turn each 3x3 neighborhood into a column
       %  B=B+G(:)';                   % Add the weights to each row
      B(5,:)-=G(:)';         % Subtract the weights from center of neighborhood
      A=reshape(min(B),z);   % Take minimum columnwise and reshape to original
   end
   A(end)                    % Display cost of getting to last cell

Spiegazione

Data una serie di pesi:

7   12    6    2    4
5   13    3   11    1
4    7    2    9    3
4    2   12   13    4
9    2    7    9    4

Inizializza una matrice di costi in modo che il costo per raggiungere ogni elemento sia Infinito, tranne il punto iniziale (l'elemento in alto a sinistra) il cui costo è uguale al suo peso.

  7   Inf   Inf   Inf   Inf
Inf   Inf   Inf   Inf   Inf
Inf   Inf   Inf   Inf   Inf
Inf   Inf   Inf   Inf   Inf
Inf   Inf   Inf   Inf   Inf

Questa è iterazione 0. Per ogni iterazione successiva, il costo per raggiungere una cella è impostato al minimo di:

  • il costo attuale per raggiungere quell'elemento, e
  • il costo attuale per raggiungere i vicini dell'elemento + il peso dell'elemento

Dopo la prima iterazione, sarà il costo del percorso all'elemento (2,2) (usando l'indicizzazione basata su 1)

minimum([  7   Inf   Inf]   [13  13  13]) = 20
        [Inf   Inf   Inf] + [13   0  13]
        [Inf   Inf   Inf]   [13  13  13]

La matrice dei costi completa dopo la prima iterazione sarebbe:

  7    19   Inf   Inf   Inf
 12    20   Inf   Inf   Inf
Inf   Inf   Inf   Inf   Inf
Inf   Inf   Inf   Inf   Inf
Inf   Inf   Inf   Inf   Inf

Dopo l'iterazione k, ciascun elemento sarà il costo più basso per raggiungere quell'elemento dall'inizio eseguendo la maggior parte dei kpassaggi. Ad esempio, l'elemento in (3,3) può essere raggiunto in 2 passaggi (iterazioni) per un costo di 22:

  7    19    25   Inf   Inf
 12    20    22   Inf   Inf
 16    19    22   Inf   Inf
Inf   Inf   Inf   Inf   Inf
Inf   Inf   Inf   Inf   Inf

Ma al 4 ° iterazione, viene trovato un percorso di 4 passaggi con un costo di 20:

 7   19   25   24   28
12   20   22   32   25
16   19   20   30   34
20   18   30   34   35
27   20   25   40   39

Poiché nessun percorso attraverso la matrice mxn può essere più lungo del numero di elementi nella matrice (come limite superiore molto lento), dopo le m*niterazioni ogni elemento conterrà il costo del percorso più breve per raggiungere quell'elemento dall'inizio.


-1 byte rimuovendo lo spazio tra whilee ~.
notjagan,

@notjagan sono passato da whilea fored era ancora in grado di usare la punta. Grazie!
becher

5

JavaScript, 197 byte

a=>(v=a.map(x=>x.map(_=>1/0)),v[0][0]=a[0][0],q=[...(a+'')].map(_=>v=v.map((l,y)=>l.map((c,x)=>Math.min(c,...[...'012345678'].map(c=>a[y][x]+((v[y+(c/3|0)-1]||[])[x+c%3-1]||1/0)))))),v.pop().pop())

Abbellire:

a=>(
  // v is a matrix holds minimal distance to the left top
  v=a.map(x=>x.map(_=>1/0)),
  v[0][0]=a[0][0],
  q=[
     // iterate more than width * height times to ensure the answer is correct
    ...(a+'')
  ].map(_=>
    v=v.map((l,y)=>
      l.map((c,x)=>
        // update each cell
        Math.min(c,...[...'012345678'].map(
          c=>a[y][x]+((v[y+(c/3|0)-1]||[])[x+c%3-1]||1/0)
        ))
      )
    )
  ),
  // get result at right bottom
  v.pop().pop()
)

4

Mathematica 279 byte

L'idea di base è quella di creare un grafico con vertici corrispondenti alle voci della matrice e ai bordi diretti tra due vertici separati da un ChessboardDistancemaggiore di zero ma inferiore o uguale a 1. Per inciso, questo è noto come un grafico King , poiché corrisponde a le mosse valide di un re su una scacchiera.

FindShortestPathviene quindi utilizzato per ottenere il percorso minimo. Funziona su EdgeWeight, no VertexWeight, quindi c'è qualche codice aggiuntivo per definire la EdgeWeightvoce della matrice corrispondente alla destinazione di ogni fronte diretto.

Codice:

(m=Flatten[#];d=Dimensions@#;s=Range[Times@@d];e=Select[Tuples[s,2],0<ChessboardDistance@@(#/.Thread[s->({Ceiling[#/d[[1]]],Mod[#,d[[1]],1]}&/@s)])≤1&];Tr[FindShortestPath[Graph[s,#[[1]]->#[[2]]&/@e,EdgeWeight->(Last@#&/@Map[Extract[m,#]&,e,{2}])],1,Last@s]/.Thread[s->m]])&

Nota che il personaggio è il simbolo di trasposizione. Incollerà così com'è in Mathematica.

Uso:

%@{{2, 55, 5, 3, 1, 1, 4, 1},
  {2, 56, 1, 99, 99, 99, 99, 5},
  {3, 57, 5, 2, 2, 2, 99, 1},
  {3, 58, 4, 2, 8, 1, 99, 2},
  {4, 65, 66, 67, 68, 3, 99, 3},
  {2, 5, 4, 3, 3, 4, 99, 5},
  {75, 76, 77, 78, 79, 80, 81, 2},
  {5, 4, 5, 1, 1, 3, 3, 2}}

Produzione:

67

Se si imposta g=Graph[...,GraphLayout->{"GridEmbedding","Dimension"->d},VertexLabels->Thread[s->m]e p=FindShortestPath[...quindi il seguente grafico visualizzerà visivamente la soluzione (la parte superiore della matrice corrisponde alla parte inferiore del grafico):

HighlightGraph[g,PathGraph[p,Thread[Most@p->Rest@p]]]

inserisci qui la descrizione dell'immagine


3

Haskell, 228 byte

Le posizioni sono elenchi di due elementi, perché sono facili da generare sequencee altrettanto facili da abbinare come 2 tuple.

h=g[[-1,-1]]
g t@(p:r)c|p==m=0|1<2=minimum$(sum$concat c):(\q@[a,b]->c!!a!!b+g(q:t)c)#(f(e$s$(\x->[0..x])#m)$f(not.e t)$zipWith(+)p#s[[-1..1],[-1..1]])where m=[l(c)-1,l(head c)-1]
(#)=map
f=filter
e=flip elem
s=sequence
l=length

Inizia da -1,-1e conta il costo di ogni campo di destinazione dei passi.

Prime due righe alternative: inizia alle 0,0, conta i campi di partenza, termina alle coordinate uguali alle dimensioni della matrice (quindi in basso a destra rispetto all'obiettivo, che deve essere aggiunto all'elenco delle destinazioni legali) - stessa lunghezza ma più lenta:

j=i[[0,0]]
i t@(p@[a,b]:r)c|p==m=0|1<2=c!!a!!b+(minimum$(sum$concat c):(\q->i(q:t)c)#(f(e$m:(s$(\x->[0..x-1])#m))$f(not.e t)$zipWith(+)p#s[[-1..1],[-1..1]]))where m=[l c,l$head c]

L'uso di un infix per mapnon salva i byte qui, ma lo sostituisco non appena non costa uno, perché può solo migliorare con più usi e talvolta anche con altre ristrutturazioni che rasano un'altra coppia di parentesi.

Da migliorare: ridondanti filters. Unire / allineare a filter(flip elem$(s$(\x->[0..x])#m)\\p)con import Data.Listper \\3 byte di costi.

Inoltre, troppo male (fromEnumTo 0)è 2 byte più lungo di (\x->[0..x]).

sum$concat cè il costo di tutti i campi sommato e quindi un limite superiore concisamente esprimibile sul costo del percorso che viene dato per minimumevitare un elenco vuoto (il mio tipo di controllo ha già determinato il tutto su cui lavorare su Integers, quindi nessun hard coding il massimo , hehe). Indipendentemente dal modo in cui restringo i passaggi basati su quello precedente (che accelererebbe molto l'algoritmo, ma anche costi di byte), non posso evitare i vicoli ciechi che rendono necessario questo fallback.

  • Un'idea di filtro era ((not.e n).zipWith(-)(head r))con l'estrazione n=s[[-1..1],[-1..1]], che richiede l'aggiunta ,[-1,-1]al percorso iniziale. L'algoritmo quindi evita di andare dove potrebbe già essere andato nel passaggio precedente, il che rende un vicolo cieco avanzare su un campo di bordo ortogonalmente a quel bordo.

  • Un altro era ((>=0).sum.z(*)d)con l'estrazione z=zipWith, che introduce un nuovo argomento dnella funzione ricorsiva che viene data come (z(-)p q)nella ricorsione e [1,1]nel caso iniziale. L'algoritmo evita i passaggi successivi con un prodotto scalare negativo ( dessendo il passaggio precedente), il che significa che non si verificano curve brusche a 45 °. Ciò restringe ancora considerevolmente le scelte ed evita il banale vicolo cieco precedente, ma ci sono ancora percorsi che finiscono racchiusi in campi già visitati (e forse una 'fuga' che tuttavia sarebbe una svolta brusca).


3

Python 2, 356 320 byte

s=input()
r=lambda x:[x-1,x,x+1][-x-2:]
w=lambda z:[z+[(x,y)]for x in r(z[-1][0])for y in r(z[-1][1])if x<len(s)>0==((x,y)in z)<len(s[0])>y]
l=len(s)-1,len(s[0])-1
f=lambda x:all(l in y for y in x)and x or f([a for b in[l in z and[z]or w(z)for z in x]for a in b])
print min(sum(s[a][b]for(a,b)in x)for x in f([[(0,0)]]))

Provalo qui!

-36 byte grazie a notjagan !

Riceve un elenco di elenchi come input e genera il costo più basso durante la navigazione della matrice da in alto a sinistra a in basso a destra.

Spiegazione

Trova tutti i percorsi possibili dalla parte superiore sinistra alla parte inferiore destra della matrice, creando un elenco di coordinate x, y per ciascun percorso. I percorsi non possono tornare indietro e devono terminare a (len(s)-1,len(s[0])-1).

Somma gli interi su ciascun percorso delle coordinate e restituisci il costo minimo.

È printpossibile modificarlo facilmente per visualizzare l'elenco delle coordinate per il percorso più breve.


-36 byte con alcune modifiche varie.
notjagan,

@notjagan Grandi cambiamenti, in particolare l'uso di orper i condizionali. Grazie!
Risoluzione

1

APL (Dyalog Classic) , 33 byte

{⊃⌽,(⊢⌊⍵+(⍉3⌊/⊣/,⊢,⊢/)⍣2)⍣≡+\+⍀⍵}

Provalo online!

{ } funzione con argomento

+\+⍀⍵ prendere somme parziali per riga e per colonna per stabilire un limite superiore pessimistico sulle distanze del percorso

( )⍣≡ ripetere fino alla convergenza:

  • (⍉3⌊/⊣/,⊢,⊢/)⍣2min delle distanze ai vicini, ovvero fare due volte ( ( )⍣2): anteporre la colonna più a sinistra ( ⊣/,) a self ( ) e aggiungere le colonne più a destra ( ,⊢/), trovare i minimi in triple orizzontali ( 3⌊/) e trasporre ( )

  • ⍵+ aggiungi il valore di ciascun nodo al suo minimo di distanze ai vicini

  • ⊢⌊ prova a battere le migliori distanze attuali

⊃⌽, infine, restituisce la cella in basso a destra

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.