Risolvi il problema di trovare il percorso più breve usando l'algoritmo di Dijkstra.
Trova il percorso più breve da X0 a X7. Il grafico è dato dagli elementi della matrice dei costi
Costruiamo questo grafico
Cominciamo con l'elemento X0 e assegniamo un'etichetta 0, da allora consideriamo tutti i suoi vicini non ci sono ancora segni, quindi assegniamo loro le lunghezze appropriate:
Tutti i vicini X0 sono stati considerati, lo contrassegniamo e andiamo al vertice X1. I suoi vicini X0, X2, X4, ma X0 è contrassegnato, non lo consideriamo. Per X2: , lascia l'etichetta.
Per X4: , sostituire l'etichetta. Tutti i vicini del vertice X1 sono considerati, lo contrassegniamo
vai in cima X2. I suoi vicini X0, X1, X3, X4, X5, X6, ma X0, X1 sono contrassegnati, non li consideriamo.
Per X3: , lascia l'etichetta.
Per X5: , sostituire l'etichetta.
Per X4: , lascia l'etichetta.
Per X6: , sostituire l'etichetta.
Tutti i vicini di vertice X2 sono stati considerati, lo contrassegniamo.
vai in cima X3. I suoi vicini X0, X2, X6, ma X0, X2 sono contrassegnati, non li consideriamo.
Per X6: , lascia l'etichetta.
Tutti i vicini di vertice X3 sono stati considerati, lo contrassegniamo.
vai in alto X4. I suoi vicini X1, X2, X5, X7, ma X1, X2 sono contrassegnati, non li consideriamo.
Per X5: , sostituire l'etichetta.
Per X7: , sostituire l'etichetta.
Tutti i vicini di vertice X4 sono stati considerati, lo contrassegniamo.
vai in cima X5. I suoi vicini X2, X4, X6, X7, ma X2, X4 sono contrassegnati, non li consideriamo.
Per X6: , lascia l'etichetta.
Per X7: , lascia l'etichetta.
Tutti i vicini di vertice X5 sono stati considerati, lo contrassegniamo.
vai in cima X6. I suoi vicini X2, X3, X5, X7, ma X2, X3, X5 sono contrassegnati, non li consideriamo.
Per X7: , lascia l'etichetta.
Tutti i vicini di vertice X6 sono stati considerati, lo contrassegniamo. E contrassegniamo la X7 rimanente, vengono considerati tutti i vertici.
Conclusione: il percorso più breve da X0 a X7 ha una lunghezza di 101, questo percorso: X7-X4-X1-X0.
L'algoritmo di Dijkstra è un algoritmo grafico inventato dallo scienziato olandese Edsger Dijkstra nel 1959. Trova i percorsi più brevi da uno dei vertici del grafico a tutti gli altri. L'algoritmo funziona solo per grafici senza spigoli di peso negativo.
Consideriamo l'esecuzione dell'algoritmo usando l'esempio del grafico mostrato in figura.
Lascia che sia richiesto di trovare le distanze più brevi dal 1o vertice a tutti gli altri.
I cerchi indicano i vertici, le linee - i percorsi tra di loro (i bordi del grafico). I numeri dei vertici sono indicati in cerchi, il loro "prezzo" è indicato sopra i bordi - la lunghezza del percorso. Accanto a ciascun vertice è presente un'etichetta rossa: la lunghezza del percorso più breve per questo vertice dal vertice 1.
Primo passo... Considera un passo dell'algoritmo di Dijkstra per il nostro esempio. Il vertice 1 ha l'etichetta minima, mentre i vicini sono vertici 2, 3 e 6.
Il primo vicino a sua volta del vertice 1 è il vertice 2, poiché la lunghezza del percorso è minima. La lunghezza del percorso attraverso il vertice 1 è uguale alla somma del valore dell'etichetta del vertice 1 e la lunghezza del bordo va dal 1 ° al 2 °, ovvero 0 + 7 \u003d 7. Questo è inferiore all'etichetta corrente del vertice 2, infinito, quindi la nuova etichetta del 2 ° i vertici sono 7.
Eseguiamo un'operazione simile con altri due vicini del primo vertice: il terzo e il sesto.
Tutti i vicini di vertice 1 sono controllati. L'attuale distanza minima dal picco 1 è considerata definitiva e non è soggetta a revisione (il fatto che sia proprio così è stato dimostrato per la prima volta da E. Dijkstra). Eliminiamolo dal grafico per contrassegnare che questo vertice è stato visitato.
Secondo passo... Il passaggio dell'algoritmo viene ripetuto. Trova di nuovo il “più vicino” dei vertici non visitati. Questo è il vertice 2, etichettato 7.
Proviamo di nuovo a ridurre le etichette dei vicini del vertice selezionato, provando a passare attraverso il secondo vertice in essi. I vicini del vertice 2 sono i vertici 1, 3 e 4.
Il primo (in ordine) vicino del vertice 2 è il vertice 1. Ma è già stato visitato, quindi non facciamo nulla con il primo vertice.
Il prossimo vicino del vertice 2 è il vertice 3, poiché ha l'etichetta minima dei vertici contrassegnata come non visitata. Se ci vai attraverso 2, la lunghezza di tale percorso sarà 17 (7 + 10 \u003d 17). Ma l'etichetta corrente del terzo vertice è 9, che è inferiore a 17, quindi l'etichetta non cambia.
Un altro vicino di vertice 2 è il vertice 4. Se ci vai attraverso il 2 °, allora la lunghezza di tale percorso sarà uguale alla somma della distanza più breve al 2 ° vertice e la distanza tra i vertici 2 e 4, cioè 22 (7 + 15 \u003d 22) ... Dal 22<, устанавливаем метку вершины 4 равной 22.
Tutti i vicini di vertice 2 sono stati visualizzati, congelare la distanza e contrassegnarlo come visitato.
Terzo passo... Ripetiamo il passaggio dell'algoritmo selezionando il vertice 3. Dopo la sua "elaborazione" otteniamo i seguenti risultati:
Ulteriori passi... Ripetiamo il passaggio dell'algoritmo per i vertici rimanenti. Questi saranno i vertici 6, 4 e 5, rispettivamente.
Completamento dell'esecuzione dell'algoritmo... L'algoritmo termina il suo lavoro quando non è possibile elaborare più vertici. In questo esempio, tutti i vertici vengono barrati, ma è un errore credere che ciò accadrà in ogni esempio: alcuni vertici potrebbero rimanere non incrociati se non possono essere raggiunti, cioè se il grafico è disconnesso. Il risultato dell'algoritmo può essere visto nell'ultima figura: il percorso più breve dall'alto 1 a 2 è 7, a 3 è 9, a 4 è 20, a 5 è 20, a 6 è 11.
Implementazione dell'algoritmo in vari linguaggi di programmazione:
C ++
#include "stdafx.h" #includePascal
programma DijkstraAlgorithm; usa crt; const V \u003d 6; inf \u003d 100000; digitare vektor \u003d array di numeri interi; var start: intero; const GR: array di numero intero \u003d ((0, 1, 4, 0, 2, 0), (0, 0, 0, 9, 0, 0), (4, 0, 0, 7, 0, 0), (0, 9, 7, 0, 0, 2), (0, 0, 0, 0, 0, 8), (0, 0, 0, 0, 0, 0)); (Algoritmo di Dijkstra) procedura Dijkstra (GR: array di numeri interi; st: numero intero); var count, index, i, u, m, min: intero; distanza: vektor; visitato: array di booleani; inizia m: \u003d st; per i: \u003d da 1 a V inizia la distanza [i]: \u003d inf; visitato [i]: \u003d false; fine; distanza: \u003d 0; per conteggio: \u003d da 1 a V-1 inizia min: \u003d inf; per i: \u003d 1 a V fare se (non visitato [i]) e (distanza [i]<=min) then begin min:=distance[i]; index:=i; end; u:=index; visited[u]:=true; for i:=1 to V do if (not visited[i]) and (GR<>0) e (distanza [u]<>inf) e (distanza [u] + GRGiava
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.StringTokenizer; public class Solution (private static int INF \u003d Integer.MAX_VALUE / 2; private int n; // il numero di vertici nella digraph private int m; // il numero di archi nella digraph private ArrayListUn'altra opzione:
Importa java.io. *; import java.util. *; classe pubblica Dijkstra (finale statico privato Graph.Edge GRAPH \u003d (nuovo Graph.Edge ("a", "b", 7), nuovo Graph.Edge ("a", "c", 9), nuovo Graph.Edge ( "a", "f", 14), nuovo Graph.Edge ("b", "c", 10), nuovo Graph.Edge ("b", "d", 15), nuovo Graph.Edge ("c "," d ", 11), nuovo Graph.Edge (" c "," f ", 2), nuovo Graph.Edge (" d "," e ", 6), nuovo Graph.Edge (" e ", "f", 9),); Stringa finale statica privata START \u003d "a"; Stringa finale statica privata END \u003d "e"; void statico pubblico principale (String args) (Grafico g \u003d nuovo grafico (GRAPH); g.dijkstra (START); g.printPath (END); //g.printAllPaths ();)) grafico di classe (mappa finale privata
C
#includerePHP
$ edge, "cost" \u003d\u003e $ edge); $ neighbors [$ edge] \u003d array ("end" \u003d\u003e $ edge, "cost" \u003d\u003e $ edge); ) $ vertices \u003d array_unique ($ vertices); foreach ($ vertici come $ vertice) ($ dist [$ vertice] \u003d INF; $ precedente [$ vertice] \u003d NULL;) $ dist [$ source] \u003d 0; $ Q \u003d $ vertici; while (count ($ Q)\u003e 0) (// TODO - Trova un modo più veloce per ottenere $ min \u003d INF; foreach ($ Q come $ vertice) (if ($ dist [$ vertex]< $min) { $min = $dist[$vertex]; $u = $vertex; } } $Q = array_diff($Q, array($u)); if ($dist[$u] == INF or $u == $target) { break; } if (isset($neighbours[$u])) { foreach ($neighbours[$u] as $arr) { $alt = $dist[$u] + $arr["cost"]; if ($alt < $dist[$arr["end"]]) { $dist[$arr["end"]] = $alt; $previous[$arr["end"]] = $u; } } } } $path = array(); $u = $target; while (isset($previous[$u])) { array_unshift($path, $u); $u = $previous[$u]; } array_unshift($path, $u); return $path; } $graph_array = array(array("a", "b", 7), array("a", "c", 9), array("a", "f", 14), array("b", "c", 10), array("b", "d", 15), array("c", "d", 11), array("c", "f", 2), array("d", "e", 6), array("e", "f", 9)); $path = dijkstra($graph_array, "a", "e"); echo "path is: ".implode(", ", $path)."\n";
Pitone
da collezioni importare namedtuple, coda da pprint import pprint come pp inf \u003d float ("inf") Edge \u003d namedtuple ("Edge", "start, end, cost") class Graph (): def __init __ (self, edge): self .edges \u003d edge2 \u003d self.vertices \u003d set (sum ((for e in edge2),)) def dijkstra (self, source, dest): asserisci sorgente in self.vertices dist \u003d (vertice: inf per vertice in self.vertices ) precedente \u003d (vertice: nessuno per vertice in self.vertices) dist \u003d 0 q \u003d self.vertices.copy () neighbors \u003d (vertice: set () per vertice in self.vertices) per inizio, fine, costo in sé. bordi: neighbors.add ((fine, costo)) #pp (vicini) mentre q: u \u003d min (q, chiave \u003d lambda vertice: dist) q.remove (u) se dist [u] \u003d\u003d inf o u \u003d \u003d dest: pausa per v, costo in vicini [u]: alt \u003d dist [u] + costo se alt< dist[v]: # Relax (u,v,a)
dist[v] = alt
previous[v] = u
#pp(previous)
s, u = deque(), dest
while previous[u]:
s.pushleft(u)
u = previous[u]
s.pushleft(u)
return s
graph = Graph([("a", "b", 7), ("a", "c", 9), ("a", "f", 14), ("b", "c", 10),
("b", "d", 15), ("c", "d", 11), ("c", "f", 2), ("d", "e", 6),
("e", "f", 9)])
pp(graph.dijkstra("a", "e"))
Output:
["a", "c", "d", "e"]
Tra i molti algoritmi per trovare i percorsi più brevi su un grafico, su Habré ho trovato solo una descrizione dell'algoritmo Floyd-Warshall. Questo algoritmo trova i percorsi più brevi tra tutti i vertici del grafico e la loro lunghezza. In questo articolo descriverò come funziona l'algoritmo di Dijkstra, che trova le rotte ottimali e la loro lunghezza tra un vertice specifico (sorgente) e tutti gli altri vertici del grafico. Lo svantaggio di questo algoritmo è che non funzionerà correttamente se il grafico ha archi di peso negativo.
Ad esempio, prendi il seguente grafico diretto G:
Possiamo rappresentare questo grafico come una matrice C:
Prendiamo il vertice 1 come fonte, questo significa che cercheremo le rotte più brevi dal vertice 1 ai vertici 2, 3, 4 e 5.
Questo algoritmo scorre attraverso tutti i vertici del grafico e assegna loro le etichette, che sono la distanza minima nota dal vertice di origine a un vertice specifico. Consideriamo questo algoritmo con l'esempio.
Assegniamo al primo vertice un'etichetta uguale a 0, perché questo vertice è la fonte. Il resto dei vertici sarà etichettato con infinito.
Quindi, scegliamo un vertice W con un'etichetta minima (ora è il vertice 1) e consideriamo tutti i vertici verso i quali esiste un percorso dal vertice W che non contiene vertici intermedi. Per ciascuno dei vertici considerati, assegniamo un'etichetta uguale alla somma dell'etichetta W e alla lunghezza del percorso da W al vertice considerato, ma solo se la somma risultante è inferiore al valore dell'etichetta precedente. Se l'importo non è inferiore, lasciamo invariata l'etichetta precedente.
Dopo aver considerato tutti i vertici verso i quali esiste un percorso diretto da W, contrassegniamo il vertice W come visitato e selezioniamo da quello non ancora visitato che ha il valore minimo dell'etichetta, sarà il prossimo vertice W. In questo caso, è il vertice 2 o 5. Se ci sono diversi vertici con le stesse etichette, non importa quale scegliamo come W.
Sceglieremo il vertice 2. Ma non esiste un singolo percorso in uscita da esso, quindi contrassegniamo immediatamente questo vertice come visitato e passiamo al vertice successivo con l'etichetta minima. Questa volta, solo il vertice 5 ha un'etichetta minima. Considera tutti i vertici a cui sono presenti percorsi diretti 5, ma che non sono stati ancora contrassegnati come visitati. Troviamo di nuovo la somma dell'etichetta del vertice W e il peso del bordo da W al vertice corrente, e se questa somma è inferiore all'etichetta precedente, sostituiamo il valore dell'etichetta con la somma risultante.
In base all'immagine, possiamo vedere che i segni della 3a e 4a vetta sono diventati più piccoli, cioè è stato trovato un percorso più breve verso queste cime dalla cima della sorgente. Quindi, contrassegnare il quinto vertice come visitato e selezionare il vertice successivo con l'etichetta minima. Ripetiamo tutti i passaggi precedenti fino a quando non ci sono vertici non visitati.
Dopo aver completato tutti i passaggi, otteniamo il seguente risultato:
C'è anche un vettore P, dal quale è possibile creare i percorsi più brevi. In base al numero di elementi, questo vettore è uguale al numero di vertici nel grafico: ogni elemento contiene l'ultimo vertice intermedio sul percorso più breve tra il vertice di origine e il vertice finale. All'inizio dell'algoritmo, tutti gli elementi del vettore P sono uguali al vertice di origine (nel nostro caso, P \u003d (1, 1, 1, 1, 1)). Inoltre, nella fase di ricalcolo del valore dell'etichetta per il vertice considerato, se l'etichetta del vertice considerato cambia in uno più piccolo, scriviamo il valore dell'attuale vertice W sull'array P. Ad esempio: il 3o vertice aveva un'etichetta con il valore "30", con W \u003d 1. Inoltre, in W \u003d 5, l'etichetta del 3o vertice è cambiata in "20", pertanto, scriveremo il valore nel vettore Р - Р \u003d 5. Inoltre, quando W \u003d 5, il valore dell'etichetta al 4 ° vertice è cambiato (era "50", è diventato "40"), il che significa che il valore di W - P \u003d 5 deve essere assegnato al 4 ° elemento del vettore P. Di conseguenza, otteniamo il vettore P \u003d (1, 1, 5, 5, 1).
Sapendo che l'ultimo vertice intermedio sul percorso tra la sorgente e il vertice finale è scritto in ogni elemento del vettore P, possiamo ottenere la stessa rotta più breve.
percorso più breve oggi è un compito vitale e viene utilizzato quasi ovunque, a partire dalla ricerca del percorso ottimale tra due oggetti sul terreno (ad esempio, il percorso più breve da casa all'università), nei sistemi di pilota automatico, per trovare il percorso ottimale per il trasporto, cambiare il pacchetto di informazioni nelle reti e eccetera.Il percorso più breve viene considerato utilizzando un oggetto matematico chiamato grafico. Ricerca percorso più breve viene condotto tra due vertici nel grafico. Il risultato è un percorso, ovvero una sequenza di vertici e spigoli incidente a due vertici adiacenti e la sua lunghezza.
Considera i tre più algoritmo efficiente scoperta percorso più breve:
- l'algoritmo di Dijkstra;
- Algoritmo di Floyd;
- algoritmi di enumerazione.
Gli algoritmi indicati sono facilmente eseguibili con un piccolo numero di vertici nel grafico. Con un aumento del loro numero, l'attività di ricerca percorso più breve diventa complicato.
L'algoritmo di Dijkstra
Questo algoritmo è un algoritmo grafico inventato dallo scienziato olandese E. Dijkstroy nel 1959. L'algoritmo trova la distanza più breve da uno dei vertici del grafico a tutti gli altri e funziona solo per i grafici senza bordi di peso negativo.
A ciascun vertice viene assegnato un peso: si tratta del peso del percorso dal vertice iniziale a quello indicato. Inoltre, è possibile selezionare ciascun vertice. Se il vertice è selezionato, il percorso da esso al vertice iniziale è il più breve, in caso contrario, è temporaneo. Attraversando il grafico, l'algoritmo calcola un percorso per ciascun vertice e, se risulta essere il più breve, seleziona un vertice. Il peso di questo vertice diventa il peso del percorso. Per tutti i vicini di un dato vertice, l'algoritmo calcola anche il peso, senza evidenziarli in nessun caso. L'algoritmo termina il suo lavoro, avendo raggiunto il picco finale e pesando percorso più breve diventa il peso del vertice finale.
L'algoritmo di Dijkstra
Passaggio 1. A tutti i vertici, ad eccezione del primo, viene assegnato un peso uguale a infinito e al primo vertice viene assegnato un peso uguale a 0.
Passaggio 2. Tutti i vertici non sono selezionati.
Passaggio 3. Il primo vertice viene dichiarato corrente.
Passaggio 4. Il peso di tutti i vertici non selezionati viene ricalcolato secondo la formula: il peso di un vertice non selezionato è il numero minimo dal vecchio peso di questo vertice, la somma del peso del vertice corrente e il peso del bordo che collega il vertice corrente con quello non selezionato.
Passaggio 5. Tra i vertici non selezionati, viene cercato il vertice con il peso minimo. Se uno non viene trovato, cioè il peso di tutti i vertici è uguale all'infinito, allora il percorso non esiste. Da qui l'uscita. Altrimenti, il vertice trovato diventa quello attuale. Lei si distingue.
Passaggio 6. Se il vertice corrente è quello finale, viene trovato il percorso e il suo peso è il peso del vertice finale.
Passaggio 7. Andare al passaggio 4.
Nell'implementazione del software l'algoritmo di Dijkstra costruiamo un insieme S di vertici per i quali sono già noti i percorsi più brevi dal vertice iniziale. Ad ogni passo, uno dei vertici rimanenti viene aggiunto all'insieme S, la distanza a cui dal vertice iniziale è inferiore rispetto agli altri vertici rimanenti. In questo caso, useremo l'array D, in cui sono scritte le lunghezze percorsi più brevi per ogni vertice. Quando l'insieme S contiene tutti i vertici del grafico, l'array D conterrà le lunghezze percorsi più brevi dal vertice iniziale a ciascun vertice.
Oltre a questi array, utilizzeremo una matrice di lunghezze C, in cui l'elemento C è la lunghezza del bordo (i, j), se non vi è alcun bordo, la sua lunghezza è considerata uguale all'infinito, cioè maggiore di qualsiasi lunghezza effettiva dei bordi. In effetti, la matrice C è matrice di adiacenza, in cui tutti gli elementi zero sono sostituiti dall'infinito.
Per determinare molto
Alla fine del torneo "Capodanno", lo sponsor ha deciso di inviare per posta i premi $ m $ ai vincitori. Conoscendo il numero di partecipanti $ n $ e il tempo di consegna della posta tra alcune filiali di Ukrposhta, trova il tempo minimo dopo il quale l'ultimo dei vincitori riceverà il suo premio.
Dati in ingresso
La prima riga contiene $ 3 $ numeri: il numero di partecipanti al torneo $ n $, il numero di premi sponsor $ m $ e il numero di tempi di consegna noti tra alcune delle filiali $ k $. La riga successiva contiene i numeri dei vincitori, separati da uno spazio.
Poi arrivano linee $ k $ di $ 3 $ ciascuna con informazioni sui tempi di consegna noti tra alcune filiali nel formato: $ a $ $ b $ $ t $, dove $ a $ e $ b $ sono i numeri delle filiali, $ t $ $ (0 \\ leqslant t \\ leqslant 365) $ - tempo di consegna della posta tra di loro. L'ultima riga contiene un unico numero: il numero dell'ufficio postale da cui lo sponsor invierà i premi. È noto che $ 1 \\ leqslant n, m, a, b \\ leqslant 365 $.
Produzione
Se tutti i premi vengono consegnati ai partecipanti, stampa nella prima riga "Il buon sponsor!", E nella riga successiva - il tempo dopo il quale l'ultimo dei vincitori riceverà il suo premio. Se almeno uno dei partecipanti non riesce a ricevere un premio, stampa su una sola riga "Non è colpa dello sponsor ...". Frasi di uscita senza virgolette.
test
№ | Dati in ingresso | Produzione |
1. | 3 2 2 2 3 1 2 3 2 3 4 1 |
Il buon sponsor! 7 |
2. | 5 1 4 5 1 3 2 2 3 3 4 2 5 4 5 6 1 |
Il buon sponsor! 16 |
3. | 7 2 6 1 3 1 3 2 2 4 32 4 5 94 3 1 0 6 2 4 7 2 3 7 |
Non è colpa dello sponsor ... |
4. | 5 2 6 1 2 3 1 1 3 4 2 2 4 3 5 1 4 4 5 5 2 3 7 2 |
Il buon sponsor! 6 |