Il grafo contiene fino a 10^4 vertici e fino a 4 \cdot 10^4 archi, quindi lo memorizzeremo sotto forma di lista di adiacenza. Tutti i pesi degli archi sono positivi, quindi le distanze minime possono essere trovate con l'algoritmo di Dijkstra.
Formulazione del problema in termini di grafo. Indichiamo con la lunghezza del cammino minimo tra i vertici e . Se il server si trova nella città , l'ospite della città c_i ottiene un ritardo d(c_i, v). Il ritardo totale di tutti gli ospiti è uguale a
Bisogna trovare il vertice con il valore minimo di , e in caso di parità — con il numero più piccolo.
Perché avviamo Dijkstra dalle città degli ospiti. Si potrebbe, per ogni vertice candidato , eseguire Dijkstra a partire da e sommare le distanze verso i città degli ospiti. Ma i candidati sono fino a 10^4, e un numero così elevato di esecuzioni è eccessivo. Notiamo che il grafo non è orientato, quindi d(c_i, v) = d(v, c_i). Ciò significa che una singola esecuzione di Dijkstra dalla città dell'ospite c_i fornisce immediatamente il ritardo di questo ospite per tutti i possibili luoghi in cui collocare il server. Gli ospiti non sono più di , quindi bastano esecuzioni.
Come si accumula la somma. Creiamo un array , inizialmente riempito di zeri. L'elemento conterrà il ritardo totale degli ospiti nel caso in cui il server sia collocato nel vertice .
Poi, per ciascun ospite, a turno:
Eseguiamo l'algoritmo di Dijkstra a partire dal vertice c_i e otteniamo l'array dist[1..n], dove dist[v] = d(c_i, v). Questo è il ritardo dell'-esimo ospite se il server si trova nella città .
Scorriamo tutti i vertici v = 1, 2, ..., n ed eseguiamo sum[v] += dist[v]. Così aggiungiamo il contributo dell'-esimo ospite al ritardo totale per ogni possibile posizione del server.
Dopo aver elaborato tutti i ospiti, ogni elemento contiene esattamente addendi — i ritardi di tutti gli ospiti verso il vertice , cioè sum[v] = S(v).
Scelta della risposta. Esaminiamo i vertici v = 1, 2, ..., n in ordine crescente di numero e memorizziamo il vertice con la somma minima. Aggiorniamo la risposta solo in caso di disuguaglianza stretta sum[v] < best. In questo modo, se più vertici hanno la stessa somma minima, resterà il primo di essi, cioè quello con il numero più piccolo.
Il minimo viene cercato su tutti gli vertici, e non solo sulle città degli ospiti: il luogo ottimale potrebbe essere una città in cui non ci sono ospiti.
Esempio
Il grafo riportato nell'esempio ha il seguente aspetto:
Eseguiamo l'algoritmo di Dijkstra a partire dai vertici in cui risiedono gli ospiti: e . Per ciascuno di questi vertici determiniamo le distanze minime verso tutte le altre città.
Poi, per ciascun vertice del grafo, troviamo la somma delle distanze verso tutte le città con ospiti. La somma minima delle distanze è uguale a , ed è raggiunta nel vertice con il numero più piccolo .
Quindi, il server dovrebbe essere collocato nella città . La distanza totale da esso verso le città in cui risiedono gli ospiti è pari a 5 + 0 + 8 = 13.
Implementazione dell'algoritmo
Dichiariamo la costante infinito.
#define INF 0x3F3F3F3FLa struttura memorizzerà le informazioni su un arco: il vertice a cui conduce l'arco, e il peso di tale arco.
struct edge
{
int node, dist;
edge(int node, int dist) : node(node), dist(dist) {}
};Dichiariamo un comparatore per ordinare le coppie (node, dist) in ordine decrescente del valore .
bool operator< (edge a, edge b)
{
return a.dist > b.dist;
}Dichiariamo una lista di adiacenza per rappresentare il grafo.
vector<vector<edge> > g;La funzione Dijkstra implementa l'algoritmo di Dijkstra. Riceve in ingresso il grafo e il vertice iniziale . Il valore restituito è l'array , dove contiene la distanza minima dal vertice al vertice .
void Dijkstra(vector<vector<edge> >& g, vector<int>& d, int start)
{
priority_queue<edge> pq;
pq.push(edge(start, 0));
d = vector<int>(n + 1, INF);
d[start] = 0;
while (!pq.empty())
{
edge e = pq.top(); pq.pop();
int v = e.node;
if (e.dist > d[v]) continue;
for (auto e : g[v])
{
int to = e.node;
int cost = e.dist;
if (d[v] + cost < d[to])
{
d[to] = d[v] + cost;
pq.push(edge(to, d[to]));
}
}
}
}Parte principale del programma. Leggiamo i dati di input.
scanf("%d %d %d", &n, &m, &k);
c.resize(k);
for (i = 0; i < k; i++)
scanf("%d", &c[i]);Leggiamo il grafo pesato in input.
g.resize(n + 1);
for (i = 0; i < m; i++)
{
scanf("%d %d %d", &a, &b, &w);
g[a].push_back(edge(b, w));
g[b].push_back(edge(a, w));
}Eseguiamo l'algoritmo di Dijkstra a partire dalle città in cui risiedono gli ospiti.
dp.resize(n + 1);
for (i = 0; i < k; i++)
{
Dijkstra(g, dist, c[i]);
for (j = 1; j <= n; j++)
dp[j] += dist[j];
}A questo punto contiene la somma delle distanze minime dal vertice a tutte le città in cui si trovano gli ospiti. Rimane da trovare il valore minimo tra gli elementi dell'array e stampare la risposta corrispondente. La variabile memorizza il numero più piccolo del vertice in cui collocare il server.
res = INF;
for (i = 1; i <= n; i++)
{
if (dp[i] < res)
{
res = dp[i];
ptr = i;
}
}Stampiamo la risposta.
printf("%d %d\n", ptr, dp[ptr]);