Метро#68

Метро (Розбір)

Найкоротша відстань від джерела до вершини , що належить до лінії , зберігаємо в 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]);