Найкоротша відстань від джерела до вершини , що належить до лінії , зберігаємо в dist[v][l], де 1 \le v \le 70, 1 \le l \le 10. Граф представляється у вигляді списків суміжності. Для кожного ребра, крім напрямку і вартості, додатково зберігається номер лінії метро, до якої належить вершина, до якої це ребро веде.
Алгоритм починається зі станції , розташованої на лінії . Шукана величина — мінімальна вартість маршруту, за яку можна дістатися до станції , що знаходиться на лінії .
При переході між вершинами необхідно перевіряти, чи належать вони одній лінії: якщо лінії різні, до вартості додається (пересадка між лініями).
За допомогою алгоритму Дейкстри знаходимо найкоротшу відстань із стану (s, line1) до стану (f, line2).
Приклад
У наведеному прикладі потрібно знайти найкоротший маршрут від станції на лінії до станції на лінії .
Йдемо по лінії від станції до станції : хвилини.
Здійснюємо пересадку на станції з лінії на лінію : хвилина.
Йдемо по лінії від станції до станції : хвилини.
Здійснюємо пересадку на станції з лінії на лінію : хвилина.
Йдемо по лінії від станції до станції : хвилини.
Отже, весь шлях займе хвилин.
Реалізація алгоритму
Кількість вершин у графі не більше MAX = 71. Кількість ліній не більше LINES = 11.
#define MAX 71
#define LINES 11Найкоротші відстані від джерела до вершини , що належить лінії , зберігаємо в dist[v][l]. Оголосимо масив для зберігання найкоротших відстаней.
int dist[MAX][LINES];Структура node містить інформацію про ребро:
— напрямок ребра;
— вартість ребра;
— якій лінії метро ребро належить;
struct node
{
int to, cost, line;
node(int to, int cost, int line) : to(to), cost(cost), line(line) {}
};Структура node також використовується в черзі з пріоритетами, де
— поточна вершина;
— мінімальна вартість проїзду до вершини (на лінії );
— номер лінії метро, на якій знаходиться вершина ;
В черзі з пріоритетами вершини впорядковуються за зростанням вартості проїзду.
bool operator< (node a, node b)
{
return a.cost > b.cost;
}Оголосимо список суміжності для представлення графа.
vector<vector<node> > g;Основна частина програми. Читаємо вхідні дані.
scanf("%d %d", &n, &l);
g.resize(n + 1);Читаємо -ту лінію метро. Час руху між сусідніми станціями та дорівнює .
for (i = 1; i <= l; i++)
{Читаємо станцію — першу станцію -ої лінії метро. За станцією слідує станція .
scanf("%d ", &u);
while (scanf("%d%c", &v, &ch))
{Рух між станціями можливий в обох напрямках, при цьому вартість ребра дорівнює .
g[u].push_back(node(v, 2, i));
g[v].push_back(node(u, 2, i));
u = v;Як тільки досягаємо кінця рядка, завершуємо читання даних поточної лінії метро.
if (ch == '\n') break;
}
}Ініціалізуємо масив для зберігання найкоротших відстаней.
for (i = 1; i <= n; i++)
for (j = 1; j <= l; j++)
dist[i][j] = 2000000000;Читаємо інформацію про початкову та кінцеву станцію. Для кожної станції задається лінія, якій вона належить.
scanf("%d %d %d %d", &s, &line1, &f, &line2);Стартуємо зі станції , що знаходиться на лінії .
dist[s][line1] = 0;Заносимо в чергу структуру node, що містить початкову вершину на лінії . Вартість досягнення початкової вершини дорівнює .
priority_queue<node, vector<node> > pq;
pq.push(node(s, 0, line1)); // (v, cost, line)
while (!pq.empty())
{Вибираємо з черги вершину з найменшою вартістю.
int cost = pq.top().cost;
int v = pq.top().to;
int l1 = pq.top().line;
pq.pop(); // dist[v][l1] = costПеребираємо всі вершини, суміжні з .
for (auto edge: g[v])
{Розглядаємо ребро v \to to із вартістю , що належить лінії .
int to = edge.to;
int w = edge.cost;
int l2 = edge.line;Якщо рухатися по ребру v \to to, то вартість маршруту складе cost + w.
int tot_cost = cost + w;Якщо вершини та належать різним лініям, необхідно здійснити пересадку з лінії на лінію .
if (l1 != l2) tot_cost += 1;Якщо рух по ребру v \to to покращує поточне значення, оновлюємо dist[to][l2] і поміщаємо вершину в чергу (вартість її досягнення складає і вона знаходиться на лінії ).
if (tot_cost < dist[to][l2])
{
dist[to][l2] = tot_cost;
pq.push(node(to, tot_cost, l2));
}
}
}Виводимо відповідь — мінімальна вартість маршруту до вершини на лінії .
printf("%d\n", dist[f][line2]);