68. Метро

 

Схема метро состоит из n станций, расположенных l на  линиях. Каждая станция принадлежит одной или нескольким линиям (в этом случае на станции можно выполнить пересадку на любую из проходящих через неё линий). Каждая линия включает не менее двух станций и пересекается хотя бы с одной другой линией. Схема метро является связной.

Передвижение между двумя соседними станциями одной линии возможно в обоих направлениях и занимает 2 минуты. Пересадка с одной линии на другую в пределах одной станции занимает 1 минуту. Другими затратами времени можно пренебречь.

Определите минимальное время, необходимое менеджеру фирмы «Диез-продукт», чтобы добраться от станции a до здания офиса компании, расположенного рядом со станцией b.

 

Вход. В первой строке заданы два натуральных числа n и l (1 ≤ l ≤ 10). В следующих l строках указаны последовательные номера станций каждой из линий метро. В последней строке заданы номера и линии начальной и конечной станций. Все числа натуральные и не превышают 70.

 

Выход. Выведите минимальное время движения между указанными станциями.

 

Пример входа

Пример выхода

7 3

2 1 3

6 1 4 5

5 7

2 1 7 3

10

 

 

РЕШЕНИЕ

графы – алгоритм Дейкстры

 

Анализ алгоритма

Кратчайшее расстояние от источника до вершины v, принадлежащей линии l, храним в dist[v][l], где 1 v 70, 1 l 10. Граф храним в виде списков смежности. Вместе с направлением и стоимостью ребра храним номер линии метро, которой принадежит вершина куда направлено ребро.

Стартуем со станции s, находящейся на линии line1. Ответ на задачу – минимальная стоимость, за которую можно добраться до вершины f на линии line2. При движении между вершинами следует совершить проверку: если вершины принадлежат разным линиям, то следует добавить 1 (совершить пересадку).

При помощи алгоритма Дейкстры находим кратчайшее расстояния из сосотяния (s, line1) в (f, line2).

 

Пример

В приведенном примере необходимо найти кратчайший маршрут от станции 2 на линии 1 до станции 7 на линии 3.

·        Идём по линии 1 от станции 2 к станции 1: 2 минуты.

·        Совершаем пересадку на станции 1 с линии 1 на линию 21 минута.

·        Идём по линии 2 от станции 1 к станции 5: 4 минуты.

·        Совершаем пересадку на станции 5 с линии 2 на линию 3: 1 минута.

·        Идём по линии 3 от станции 5 к станции 7: 2 минуты.

Итого весь путь займет 10 минут.

 

Реализация алгоритма

Количество вершин в графе не более MAX = 71. Количество линий не более LINES = 11.

 

#define MAX 71

#define LINES 11

 

Кратчайшее расстояние от источника до вершины v, принадлежащей линии l, храним в dist[v][l]. Объявим массив кратчайших расстояний.

 

int dist[MAX][LINES];

 

Структура node содержит информацию о ребре:

·        to – направление ребра;

·        costстоимость ребра;

·        lineкакой линии метро ребро принадлежит;

 

struct node

{

  int to, cost, line;

  node(int to, int cost, int line) : to(to), cost(cost), line(line) {}

};

 

Структура node также будет использоваться в очереди с приоритетами, где

·        to – текущая вершина;

·        costминимальная стоимость проезда до вершины to (на линии line);

·        lineномер линии метро, на которой находится вершина to;

 

В очереди с приоритетами вершины сортируются по возрастанию стоимости проезда.

 

bool operator< (node a, node b)

{

  return a.cost > b.cost;

}

 

Список смежности вершин графа.

 

vector<vector<node> > g;

 

Основная часть программы. Читаем входные данные.

 

scanf("%d %d", &n, &l);

 

g.resize(n + 1);

 

Читаем i-ую линию метро. Время движения между соседними станциями u и v равно 2.

 

for (i = 1; i <= l; i++)

{

 

Читаем станцию u – первую станцию i-ой линии метро. За станцией u идет станция v.

 

  scanf("%d ", &u);

  while (scanf("%d%c", &v, &ch))

  {

 

Движение возможно в обоих направлениях. Стоимость ребра равна 2.

 

    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);

 

Стартуем со станции s, находящейся на линии line1.

 

  dist[s][line1] = 0;

 

priority_queue<node, vector<node> > pq;

 

Заносим в очередь структуру node, содержащую начальную вершину s на линии line1. Стоимость попадания в начальную вершину равна 0.

 

pq.push(node(s, 0, line1)); // (v, cost, line)

while (!pq.empty())

{

 

Извлекаем из очереди вершину v с наименьшей стоимостью.

 

  int cost = pq.top().cost;

  int v = pq.top().to;

  int l1 = pq.top().line;

  pq.pop(); // dist[v][l1] = cost

 

Перебираем вершины, связанные с v.

 

  for (i = 0; i < g[v].size(); i++)

  {

 

Имеется ребро vto стоимостью w, принадлежащее линии l2.

 

    int to = g[v][i].to;

    int w = g[v][i].cost;

    int l2 = g[v][i].line;

 

Если двигаться в to по ребру vto, то стоимость составит cost + w.

 

    int tot_cost = cost + w;

 

Если линии у вершин v и to разные, то следует совершить пересадку с линии l1 на линию l2.

 

    if (l1 != l2) tot_cost += 1;

 

Если движение по ребру vto дает лучший результат, то улучшаем значение dist[to][l2] и заносим вершину to в очередь (стоимость попадания в нее составляет tot_cost и она находится на линии l2).

 

    if (tot_cost < dist[to][l2])

    {

      dist[to][l2] = tot_cost;

      pq.push(node(to, tot_cost, l2));

    }

  }

}

 

Вывод ответа – стоимости добраться до вершины f на линии line2.

 

printf("%d\n", dist[f][line2]);