1f2a603bdadd3e7d912c77824b80fd23b4885749
resources/2018_Territoriali_soluzioni.md
... | ... | @@ -150,19 +150,36 @@ Questa soluzione dovrebbe essere molto più veloce ed è probabilmente sufficien |
150 | 150 | |
151 | 151 | #### Soluzione ottima |
152 | 152 | |
153 | -La soluzione ottimale a questo problema consiste nell'usare una variazione dell'[Algoritmo di Dijkstra](https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm). |
|
153 | +La soluzione ottimale a questo problema consiste nell'usare una variante del noto [Algoritmo di Dijkstra](https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm). Anziché sommare il peso degli archi che si attraversano, per questo problema si può considerare il massimo tra il peso del nuovo arco e il massimo peso degli archi percorsi fino a quel momento. Il concetto di "distanza" diventa quindi il peso massimo tra quelli degli archi attraversati per arrivare fino lì, nel cammino migliore (ovvero quello a "distanza" minore, che equivale al peso massimo minore). |
|
154 | + |
|
155 | +Nell'implementazione che segue il grafo viene memorizzato ed esplorato in forma implicita (senza la costruzione di liste o matrici di adiacenza), sfruttando il fatto che - a meno dei bordi - ogni punto $(x,y)$ della mappa ha come nodi vicini i punti $(x-1,y)$, $(x+1,y)$, $(x,y-1)$ e $(x,y+1)$. Il peso degli archi può essere calcolato all'occorrenza come valore assoluto della differenza delle altezze tra i due punti. |
|
156 | + |
|
154 | 157 | |
155 | 158 | ```c++ |
159 | +#include <cstring> |
|
160 | +#include <iostream> |
|
161 | +#include <queue> |
|
156 | 162 | #define INFTY 1000000000 |
157 | 163 | |
164 | +using namespace std; |
|
165 | + |
|
158 | 166 | const int MAXH = 100; |
159 | 167 | const int MAXW = 100; |
160 | 168 | |
161 | -int A[MAXH + 2][MAXW + 2]; |
|
162 | -int D[MAXH + 2][MAXW + 2]; |
|
169 | +int A[MAXH + 2][MAXW + 2]; // Matrice delle altezze |
|
170 | +int D[MAXH + 2][MAXW + 2]; // Matrice della "distanza" dal nodo di partenza |
|
163 | 171 | int H, W; |
164 | 172 | |
165 | 173 | int compute() { |
174 | + // Algoritmo di Dijkstra. |
|
175 | + |
|
176 | + // Gli elementi nella coda di priorità sono della forma (d, (x, y)) |
|
177 | + // dove d indica la distanza per raggiungere il nodo (x,y). |
|
178 | + // priority_queue mette per primi i nodi con d maggiore. Poiché invece |
|
179 | + // vogliamo estrarre per primi i nodi a distanza d minore, inseriremo |
|
180 | + // le distanze con segno negato, per "ribaltare" l'ordinamento. |
|
181 | + // Dopo l'estrazione, è sufficiente negare di nuovo il valore per riottenere |
|
182 | + // la distanza positiva originale. |
|
166 | 183 | priority_queue<pair<int, pair<int, int>>> q; |
167 | 184 | |
168 | 185 | q.push(make_pair(0, make_pair(1, 1))); |
... | ... | @@ -171,14 +188,21 @@ int compute() { |
171 | 188 | auto top = q.top(); |
172 | 189 | q.pop(); |
173 | 190 | |
191 | + // Se il nodo è già stato esplorato e la sua miglior distanza è stata calcolata, |
|
192 | + // non proseguo. |
|
174 | 193 | if (D[top.second.first][top.second.second] != INFTY) |
175 | 194 | continue; |
176 | 195 | |
196 | + // Memorizziamo la miglior "distanza" con cui è possibile raggiungere questo nodo. |
|
177 | 197 | D[top.second.first][top.second.second] = -top.first; |
178 | 198 | |
199 | + // Esploriamo i quattro vicini. |
|
179 | 200 | for (int i = -1; i <= 1; i++) { |
180 | 201 | for (int j = -1; j <= 1; j++) { |
181 | 202 | if (i * j == 0) { |
203 | + // La miglior distanza con cui si può raggiungere il nuovo nodo è il massimo tra: |
|
204 | + // - la distanza con cui si può raggiungere il nodo attuale |
|
205 | + // - il peso del nuovo arco |
|
182 | 206 | q.push(make_pair( |
183 | 207 | -max(-top.first, |
184 | 208 | abs(A[top.second.first][top.second.second] - |