LA CAMPANA

C'è chi legge questa notizia prima di te.
Iscriviti per ricevere gli ultimi articoli.
E-mail
Nome
Cognome
Come vuoi leggere The Bell
Niente spam

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" #include usando lo spazio dei nomi std; const int V \u003d 6; // L'algoritmo di Dijkstra annulla Dijkstra (int GR [V] [V], int st) (int distance [V], count, index, i, u, m \u003d st + 1; bool visitato [V]; per (i \u003d 0; i "< "<\u003e "; cin \u003e\u003e start; Dijkstra (GR, start-1); system (" pause \u003e\u003e void ");)

Pascal

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] + GR inf quindi writeln (m, "\u003e", i, "\u003d", distanza [i]) else writeln (m, "\u003e", i, "\u003d", "route non disponibile"); fine; (blocco di programma principale) inizia clrscr; write ("Avvia vertice \u003e\u003e"); leggi (inizio); Dijkstra (GR, inizio); fine.

Giava

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 ArrayList adj; // ArrayList privato dell'elenco di adiacenza peso; // peso del bordo nel digrafo privato booleano usato; // array per la memorizzazione di informazioni sui vertici passati e non passati private int dist; // array per memorizzare la distanza dal vertice iniziale // array di antenati necessario per ripristinare il percorso più breve dal vertice iniziale int privato; int start; // vertice di partenza, da cui si cerca la distanza da tutti gli altri privato BufferedReader cin; privato PrintWriter cout; tokenizer privato StringTokenizer; // procedura per avviare l'algoritmo di Dijkstra dal vertice iniziale void privato dejkstra (int s) (dist [s] \u003d 0; // la distanza più breve dal vertice iniziale è 0 per (int iter \u003d 0; iter< n; ++iter) { int v = -1; int distV = INF; //выбираем вершину, кратчайшее расстояние до которого еще не найдено for (int i = 0; i < n; ++i) { if (used[i]) { continue; } if (distV < dist[i]) { continue; } v = i; distV = dist[i]; } //рассматриваем все дуги, исходящие из найденной вершины for (int i = 0; i < adj[v].size(); ++i) { int u = adj[v].get(i); int weightU = weight[v].get(i); //релаксация вершины if (dist[v] + weightU < dist[u]) { dist[u] = dist[v] + weightU; pred[u] = v; } } //помечаем вершину v просмотренной, до нее найдено кратчайшее расстояние used[v] = true; } } //процедура считывания входных данных с консоли private void readData() throws IOException { cin = new BufferedReader(new InputStreamReader(System.in)); cout = new PrintWriter(System.out); tokenizer = new StringTokenizer(cin.readLine()); n = Integer.parseInt(tokenizer.nextToken()); //считываем количество вершин графа m = Integer.parseInt(tokenizer.nextToken()); //считываем количество ребер графа start = Integer.parseInt(tokenizer.nextToken()) - 1; //инициализируем списка смежности графа размерности n adj = new ArrayList[n]; for (int i = 0; i < n; ++i) { adj[i] = new ArrayList(); ) // inizializzazione dell'elenco, che memorizza i pesi dei bordi weight \u003d new ArrayList [n]; per (int i \u003d 0; i< n; ++i) { weight[i] = new ArrayList(); ) // leggi il grafico specificato dall'elenco dei bordi per (int i \u003d 0; i< m; ++i) { tokenizer = new StringTokenizer(cin.readLine()); int u = Integer.parseInt(tokenizer.nextToken()); int v = Integer.parseInt(tokenizer.nextToken()); int w = Integer.parseInt(tokenizer.nextToken()); u--; v--; adj[u].add(v); weight[u].add(w); } used = new boolean[n]; Arrays.fill(used, false); pred = new int[n]; Arrays.fill(pred, -1); dist = new int[n]; Arrays.fill(dist, INF); } //процедура восстановления кратчайшего пути по массиву предком void printWay(int v) { if (v == -1) { return; } printWay(pred[v]); cout.print((v + 1) + " "); } //процедура вывода данных в консоль private void printData() throws IOException { for (int v = 0; v < n; ++v) { if (dist[v] != INF) { cout.print(dist[v] + " "); } else { cout.print("-1 "); } } cout.println(); for (int v = 0; v < n; ++v) { cout.print((v + 1) + ": "); if (dist[v] != INF) { printWay(v); } cout.println(); } cin.close(); cout.close(); } private void run() throws IOException { readData(); dejkstra(start); printData(); cin.close(); cout.close(); } public static void main(String args) throws IOException { Solution solution = new Solution(); solution.run(); } }

Un'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 grafico; // mapping dei nomi dei vertici su oggetti Vertex, costruito da un insieme di bordi / ** Un bordo del grafico (usato solo dal costruttore del grafico) * / public static class Edge (public final String v1, v2; public final int dist; public Edge (String v1, String v2, int dist) (this.v1 \u003d v1; this.v2 \u003d v2; this.dist \u003d dist;)) / ** Un vertice del grafico, completo di mappature ai vertici vicini * / public static class Il vertice implementa Comparable (nome stringa finale pubblico; pubblico int dist \u003d Integer.MAX_VALUE; // MAX_VALUE assunto come infinito pubblico Vertex precedente \u003d null; mappa finale pubblica vicini \u003d nuova HashMap<>(); public Vertex (String name) (this.name \u003d name;) private void printPath () (if (this \u003d\u003d this.previous) (System.out.printf ("% s", this.name);) else if ( this.previous \u003d\u003d null) (System.out.printf ("% s (unreached)", this.name);) else (this.previous.printPath (); System.out.printf ("-\u003e% s ( % d) ", this.name, this.dist);)) public int compareTo (Vertex altro) (return Integer.compare (dist, other.dist);)) / ** Crea un grafico da una serie di bordi * / public Graph (Edge edge) (graph \u003d new HashMap<>(Edges.length); // un passaggio per trovare tutti i vertici di (Edge e: edge) (if (! graph.containsKey (e.v1)) graph.put (e.v1, new Vertex (e.v1)); if (! graph. contieneKey (e.v2)) graph.put (e.v2, new Vertex (e.v2));) // un altro passaggio per impostare vertici vicini per (Edge e: edge) (graph.get (e.v1). neighbors.put (graph.get (e.v2), e.dist); //graph.get(e.v2).neighbors.put(graph.get(e.v1), e.dist); // anche farlo per un grafico non indirizzato)) / ** Esegue dijkstra usando un vertice di origine specificato * / public void dijkstra (String startName) (if (! graph.containsKey (startName)) (System.err.printf ("Il grafico non fa" t contiene vertice di avvio \\ "% s \\" \\ n ", startName); return;) final Vertex source \u003d graph.get (startName); NavigableSet q \u003d nuovo TreeSet<>(); // imposta vertici per (Vertex v: graph.values \u200b\u200b()) (v.previous \u003d v \u003d\u003d source? source: null; v.dist \u003d v \u003d\u003d source? 0: Integer.MAX_VALUE; q.add ( v);) dijkstra (q); ) / ** Implementazione dell'algoritmo di dijkstra usando un heap binario. * / Private void dijkstra (NavigableSet finale q) (Vertice u, v; while (! q.isEmpty ()) (u \u003d q.pollFirst (); // vertice con la distanza più breve (la prima iterazione restituirà la sorgente) se (u.dist \u003d\u003d Integer.MAX_VALUE) break; // possiamo ignorare u (e tutti gli altri vertici rimanenti) poiché sono irraggiungibili // guardare le distanze da ciascun vicino per (Map.Entry a: u.neighbors.entrySet ()) (v \u003d a.getKey (); // il vicino in questa iterazione final int alternateDist \u003d u.dist + a.getValue (); if (alternateDist< v.dist) { // shorter path to neighbour found q.remove(v); v.dist = alternateDist; v.previous = u; q.add(v); } } } } /** Prints a path from the source to the specified vertex */ public void printPath(String endName) { if (!graph.containsKey(endName)) { System.err.printf("Graph doesn"t contain end vertex \"%s\"\n", endName); return; } graph.get(endName).printPath(); System.out.println(); } /** Prints the path from the source to every vertex (output order is not guaranteed) */ public void printAllPaths() { for (Vertex v: graph.values()) { v.printPath(); System.out.println(); } } }

C

#includere #includere #includere // # definisce BIG_EXAMPLE typedef struct node_t node_t, * heap_t; typedef struct edge_t edge_t; struct edge_t (node_t * nd; / * target di questo edge * / edge_t * fratello; / * per lista singolarmente collegata * / int len; / * edge edge * /); struct node_t (edge_t * edge; / * elenco di spigoli collegati singolarmente * / node_t * via; / * dove nodo precedente si trova nel percorso più breve * / double dist; / * distanza dal nodo di origine * / nome char; / * the, er , nome * / int heap_idx; / * collegamento alla posizione dell'heap per l'aggiornamento della distanza * /); / * --- gestione dei bordi --- * / #ifdef BIG_EXAMPLE # define BLOCK_SIZE (1024 * 32 - 1) #else # define BLOCK_SIZE 15 #endif edge_t * edge_root \u003d 0, * e_next \u003d 0; / * Non preoccuparti delle cose di gestione della memoria, sono oltre il punto. Fai finta e_next \u003d malloc (sizeof (edge_t)) * / void add_edge (node_t * a, node_t * b, double d) (if (e_next \u003d\u003d edge_root ) (edge_root \u003d malloc (sizeof (edge_t) * (BLOCK_SIZE + 1)); edge_root.sibling \u003d e_next; e_next \u003d edge_root + BLOCK_SIZE;) --e_next; e_next-\u003e nd \u003d b; e_next-\u003e len \u003d d; e_next -\u003e sibling \u003d a-\u003e edge; a-\u003e edge \u003d e_next;) void free_edges () (for (; edge_root; edge_root \u003d e_next) (e_next \u003d edge_root.sibling; free (edge_root);)) / * --- roba della coda di priorità --- * / heap_t * heap; int heap_len; void set_dist (node_t * nd, node_t * via, double d) (int i, j; / * conosceva già il percorso migliore * / if (nd-\u003e via && d\u003e \u003d nd-\u003e dist) return; / * trova la voce heap esistente o creane una nuova * / nd-\u003e dist \u003d d; nd-\u003e via \u003d via; i \u003d nd-\u003e heap_idx; if (! i) i \u003d ++ heap_len; / * upeap * / for (; i\u003e 1 && nd-\u003e dist< heap->dist; i \u003d j) (heap [i] \u003d heap [j]) -\u003e heap_idx \u003d i; heap [i] \u003d nd; nd-\u003e heap_idx \u003d i; ) node_t * pop_queue () (node_t * nd, * tmp; int i, j; if (! heap_len) return 0; / * rimuovi l'elemento iniziale, trascina l'elemento di coda lì e downheap * / nd \u003d heap; tmp \u003d heap; per (i \u003d 1; i< heap_len && (j = i * 2) <= heap_len; i = j) { if (j < heap_len && heap[j]->dist\u003e heap-\u003e dist) j ++; if (heap [j] -\u003e dist\u003e \u003d tmp-\u003e dist) si rompe; (heap [i] \u003d heap [j]) -\u003e heap_idx \u003d i; ) heap [i] \u003d tmp; tmp-\u003e heap_idx \u003d i; ritorno nd; ) / * --- roba Dijkstra; i nodi non raggiungibili non entreranno mai nella coda --- * / void calc_all (node_t * start) (node_t * lead; edge_t * e; set_dist (start, start, 0); while ((lead \u003d pop_queue ())) for ( e \u003d lead-\u003e edge; e; e \u003d e-\u003e sibling) set_dist (e-\u003e nd, lead, lead-\u003e dist + e-\u003e len);) void show_path (node_t * nd) (if (nd-\u003e via \u003d\u003d nd) printf ("% s", nd-\u003e nome); altrimenti if (! nd-\u003e via) printf ("% s (non raggiunto)", nd-\u003e nome); else (show_path (nd-\u003e via); printf ("-\u003e% s (% g)", nd-\u003e name, nd-\u003e dist);)) int main (void) (#ifndef BIG_EXAMPLE int i; # define N_NODES ("f" - " a "+ 1) node_t * nodes \u003d calloc (sizeof (node_t), N_NODES); per (i \u003d 0; i< N_NODES; i++) sprintf(nodes[i].name, "%c", "a" + i); # define E(a, b, c) add_edge(nodes + (a - "a"), nodes + (b - "a"), c) E("a", "b", 7); E("a", "c", 9); E("a", "f", 14); E("b", "c", 10);E("b", "d", 15);E("c", "d", 11); E("c", "f", 2); E("d", "e", 6); E("e", "f", 9); # undef E #else /* BIG_EXAMPLE */ int i, j, c; # define N_NODES 4000 node_t *nodes = calloc(sizeof(node_t), N_NODES); for (i = 0; i < N_NODES; i++) sprintf(nodes[i].name, "%d", i + 1); /* given any pair of nodes, there"s about 50% chance they are not connected; if connected, the cost is randomly chosen between 0 and 49 (inclusive! see output for consequences) */ for (i = 0; i < N_NODES; i++) { for (j = 0; j < N_NODES; j++) { /* majority of runtime is actually spent here */ if (i == j) continue; c = rand() % 100; if (c < 50) continue; add_edge(nodes + i, nodes + j, c - 50); } } #endif heap = calloc(sizeof(heap_t), N_NODES + 1); heap_len = 0; calc_all(nodes); for (i = 0; i < N_NODES; i++) { show_path(nodes + i); putchar("\n"); } #if 0 /* real programmers don"t free memories (they use Fortran) */ free_edges(); free(heap); free(nodes); #endif return 0; }

PHP

$ 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

LA CAMPANA

C'è chi legge questa notizia prima di te.
Iscriviti per ricevere gli ultimi articoli.
E-mail
Nome
Cognome
Come vuoi leggere The Bell
Niente spam