Ho finalmente ottenuto 100/100! Ora però sono curioso di sapere come pulire e semplificare il mio codice, siccome è ancora piuttosto… Artigianale, ecco. Ad esempio, mi sembra inverosimile doversi ricordare il valore massimo di un long long e sicuramente avrei potuto costruire le liste di adiacenza ed i vettori delle distanze e dei nodi processati in un unico ciclo. Comunque sia, il tuo aiuto è stato preziosissimo! Grazie ancora!
Ecco il codice della mia soluzione, nel caso qualcun altro incappi nei miei stessi dubbi:
#include <vector>
#include <algorithm>
#include <queue>
using namespace std;
void mincammino(int N, int M, vector<int> X, vector<int> Y, vector<int> P, vector<long long>& D)
{
vector<vector<pair<long long, int>>> listeDiAdiacenza(N);
// Le liste di adiacenza sono il modo di rappresentare un grafo su C++.
// Ad ogni nodo corrisponde una lista.
// Nel caso dell'algoritmo di Djikstra, il grafo da analizzare è pesato sugli archi.
// Ogni lista quindi contiene delle coppie formate da due numeri, che rappresentano degli archi.
// Il primo numero corrisponde al nodo di arrivo di un arco, il secondo al suo peso.
// Possono esistere liste vuote e liste con più elementi.
// Il numero totale di liste è N, il numero dei nodi,
// mentre il numero totale di elementi nelle liste è M, il numero degli archi.
priority_queue<pair<long long, int>, vector<pair<long long, int>>, greater<pair<long long, int>>> pq{};
// Questa è una "priority queue", ossia una lista di elementi
// legati da relazioni di precedenza, come una fila di persone in un negozio.
// Serve per considerare solo gli archi necessari per la risoluzione del problema,
// risparmiando quindi tempo. Con questa sintassi, inoltre, specifico che
// deve venire data la precedenza alla coppia con il minor numero come primo elemento.
vector<char> nodoProcessato(N);
// Questo vettore serve per "far ricordare" al computer quali nodi sono già stati processati.
// Uso i char per non usare un vettore di bool
// (potrebbe comportarsi in modi inaspettati altrimenti).
vector<long long int> distanze(N);
// In questo vettore si trovano le distanze d[i] dal nodo 0 al nodo i.
// Questo ciclo 'for' serve per costruire le liste di adiacenza.
for (int i{ 0 }; i < M; ++i)
{
int nodo{ X[i] };
// Questo ciclo deve iterare una volta per ogni elemento nel vettore X.
// X è inizializzato da 0 a M-1, quindi i deve iterare da 0 a M-1.
listeDiAdiacenza[nodo].push_back({ Y[i], P[i] });
// Non ho idea se potrebbe funzionare anche con qualcosa del tipo:
// listeDiAdiacenza[X[i]].push_back({ Y[i], P[i] });
// A scanso di pericoli esplicito X[i] come variabile int, così sono sicuro che funzioni.
}
// Quello che ottengo con questo ciclo è un numero N di liste di adiacenza
// contenenti un numero totale di M coppie, che rappresentano il mio grafo.
// Con questo ciclo impongo le distanze dal nodo 0 agli altri nodi come infinite.
// Mi serve perché Djikstra opera comparando le distanze di diversi percorsi:
// se un percorso per arrivare ad un nodo è minore della distanza assegnata a tale nodo,
// L'algoritmo la sostituisce con la nuova distanza del percorso trovato.
for (int i{ 0 }; i < N; ++i)
{
distanze[i] = 9223372036854775807;
// Ci dev'essere un modo più furbo di scrivere il massimo valore di un long long...
// Qualche comando che non conosco?
}
// Con questo ciclo dico al computer che non abbiamo ancora processato alcun nodo.
for (int i{ 0 }; i < N; ++i)
{
nodoProcessato[i] = 'N';
// N sta per No, S sta per Sì, e rispondono alla domanda "i è un nodoProcessato?"
// ... Almeno, nella mia testa funziona così XD
}
distanze[0] = 0;
// La distanza del nodo di partenza da sé stesso è sicuramente 0.
pq.push({ 0, 0 });
// Nella priority queue gli elementi in coda sono
// delle coppie di numeri, determinate nel seguente modo:
// il primo numero è la distanza del nodo di cui voglio analizzare i vicini dal nodo 0,
// il secondo è il numero corrispondente a quel nodo.
// Adesso si entra nel cuore di Djikstra:
// Questo tratto di algoritmo serve per calcolare le distanze minime di tutti i nodi dal nodo 0.
while (!pq.empty())
{
int nodoA = pq.top().second;
// A è il nodo di cui voglio analizzare i vicini.
pq.pop();
// Devo cancellare l'elemento che sto analizzando, proprio come
// quando una persona in coda viene servita, poi esce dal negozio.
// (In questo caso a quanto pare esplode, ma non è un mio problema.)
if (nodoProcessato[nodoA] == 'S')
continue;
// Se ho già analizzato un nodo, non serve analizzarlo di nuovo.
// Questo è il grande pregio dell'algoritmo di Djikstra.
nodoProcessato[nodoA] = 'S';
// Una volta finito con i vicini di A, potrò dire di averlo processato:
// Quindi, tanto vale dirlo subito.
// Non ho idea di come funzioni questo particolare ciclo for, ma quello che fa è
// considerare tutti gli elementi della lista di adiacenza corrispondente al nodo A.
for (auto arcoAB : listeDiAdiacenza[nodoA])
{
long long nodoB{ arcoAB.first };
// Bisogna ricordare che l'arco considerato è una pair<int, int>,
// il cui primo elemento è il numero del nodo di arrivo (B)
// ed il secondo è il peso dell'arco che li collega.
long long pesoArco{ arcoAB.second };
if (distanze[nodoA] + pesoArco < distanze[nodoB])
{
distanze[nodoB] = distanze[nodoA] + pesoArco;
// Se l'arco che sto considerando riduce la distanza tra il nodo 0 ed il
// nodo di arrivo dell'arco, sostituisco quella distanza nel vettore delle distanze,
// nella posizione che corrisponde al nodo di arrivo dell'arco.
pq.push({ distanze[nodoB], nodoB });
// Per finire, aggiorno la priority queue:
// ogni volta che una persona esce dal negozio, tutti i vicini
// che hanno subito una modifica alla loro distanza entrano.
// Tra questi, considererò solo quelli che non sono ancora stati processati,
// e darò la precedenza al nodo con la distanza minore tra quelli in coda:
// infatti, sicuramente non esiste un percorso più breve per arrivare a lui,
// siccome usando Djikstra si assume che non esistono archi dal peso negativo.
}
}
}
// Concludo il problema inserendo le distanze finali nel vettore della soluzione.
for (int i{ 0 }; i < N; ++i)
{
D[i] = distanze[i];
if (distanze[i] == 9223372036854775807)
D[i] = -1;
}
}