Граф містить до 10^4 вершин і до 4 \cdot 10^4 ребер, тому зберігатимемо його у вигляді списку суміжності. Усі ваги ребер додатні, отже, найкоротші відстані можна знаходити алгоритмом Дейкстри.
Постановка задачі в термінах графа. Позначимо через довжину найкоротшого шляху між вершинами і . Якщо сервер стоїть у місті , то гість із міста c_i отримує затримку d(c_i, v). Сумарна затримка всіх гостей дорівнює
Потрібно знайти вершину з мінімальним значенням , а при рівності — з найменшим номером.
Чому запускаємо Дейкстру з міст гостей. Можна було б для кожної вершини-кандидата запустити Дейкстру з і скласти відстані до міст гостей. Але кандидатів до 10^4, і стільки запусків занадто багато. Зауважимо, що граф неорієнтований, тому d(c_i, v) = d(v, c_i). Отже, один запуск Дейкстри з міста гостя c_i одразу дає затримку цього гостя для всіх можливих місць сервера. Гостей не більше , достатньо запусків.
Як накопичується сума. Заведемо масив , спочатку заповнений нулями. Елемент зберігатиме сумарну затримку гостей при розміщенні сервера у вершині .
Далі для кожного гостя по черзі:
Запускаємо алгоритм Дейкстри з вершини c_i і отримуємо масив dist[1..n], де dist[v] = d(c_i, v). Це затримка -го гостя, якщо сервер знаходиться в місті .
Проходимо по всіх вершинах v = 1, 2, ..., n і виконуємо sum[v] += dist[v]. Так ми додаємо внесок -го гостя до сумарної затримки кожного можливого місця розміщення сервера.
Після обробки всіх гостей кожен елемент містить рівно доданків — затримки всіх гостей до вершини , тобто sum[v] = S(v).
Вибір відповіді. Перебираємо вершини v = 1, 2, ..., n у порядку зростання номерів і запам'ятовуємо вершину з мінімальною сумою. Відповідь оновлюємо лише при строгій нерівності sum[v] < best. Тоді при кількох вершинах з однаковою мінімальною сумою залишиться перша з них, тобто з найменшим номером.
Мінімум шукається по всіх вершинах, а не лише по містах гостей: оптимальним місцем може виявитися місто, де гостей немає.
Приклад
Граф, наведений у прикладі, має такий вигляд:
Запустимо алгоритм Дейкстри з вершин, у яких проживають гості: і . Для кожної з цих вершин визначимо найкоротші відстані до всіх інших міст.
Далі для кожної вершини графа знайдемо суму відстаней до всіх міст із гостями. Мінімальна сума відстаней дорівнює , і вона досягається у вершині з найменшим номером .
Таким чином, сервер слід розмістити в місті . Сумарна відстань від нього до міст, де проживають гості, становить 5 + 0 + 8 = 13.
Реалізація алгоритму
Оголосимо константу нескінченність.
#define INF 0x3F3F3F3FСтруктура зберігатиме інформацію про ребро: вершину , у яку веде ребро, і вагу цього ребра.
struct edge
{
int node, dist;
edge(int node, int dist) : node(node), dist(dist) {}
};Оголосимо компаратор для сортування пар (node, dist) за спаданням значення .
bool operator< (edge a, edge b)
{
return a.dist > b.dist;
}Оголосимо список суміжності для представлення графа.
vector<vector<edge> > g;Функція Dijkstra реалізує алгоритм Дейкстри. На вхід вона приймає граф і початкову вершину . Значенням, що повертається, є масив , де містить найкоротшу відстань від вершини до вершини .
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]));
}
}
}
}Основна частина програми. Читаємо вхідні дані.
scanf("%d %d %d", &n, &m, &k);
c.resize(k);
for (i = 0; i < k; i++)
scanf("%d", &c[i]);Читаємо вхідний зважений граф.
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));
}Запускаємо алгоритм Дейкстри з міст, у яких проживають гості.
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];
}На даний момент містить суму найкоротших відстаней від вершини до всіх міст, у яких знаходяться гості. Залишається знайти мінімальне значення серед елементів масиву і вивести відповідну відповідь. Змінна зберігає найменший номер вершини, у якій слід розмістити сервер.
res = INF;
for (i = 1; i <= n; i++)
{
if (dp[i] < res)
{
res = dp[i];
ptr = i;
}
}Виводимо відповідь.
printf("%d %d\n", ptr, dp[ptr]);