Месники#10648

Месники (Розбір)

За умовою задачі неможливо повернутися до початкового міста, пройшовши по декількох дорогах, тому граф доріг є орієнтованим ациклічним графом.

Задача полягає в тому, щоб знайти три різні вершини, такі що жоден шлях не проходить через усі три, або повідомити, що таких вершин не існує.

Якщо , неможливо вибрати три різні міста, і відповідь . Надалі вважаємо, що n \ge 3.

Будь-який шлях в ациклічному графі відвідує вершини в порядку їх топологічного сортування. Отже, якщо шлях містить вершини та , то одна з них досяжна з іншої. Звідси випливає ключове спостереження: якщо існують дві вершини та , такі що жодна з них не досяжна з іншої, то жоден шлях не проходить через обидві з них. Відповідно, не існує шляху і через трійку x, y, z, де — довільна третя вершина.

Таку пару вершин зручно знаходити за допомогою алгоритму топологічного сортування Кана. Спочатку всі вершини з нульовою вхідною степенню поміщаються в чергу. Потім вершини по черзі виймаються з черги та видаляються з графа разом з їхніми вихідними ребрами; вершини, вхідна степінь яких в результаті стає нульовою, додаються до черги.

Твердження 1. Якщо в деякий момент черга одночасно містить дві вершини та , то шляху між ними не існує.

Твердження 2. Якщо на кожному кроці алгоритму черга містить рівно одну вершину, то всі вершини графа лежать на одному шляху.

Таким чином, алгоритм наступний: виконуємо алгоритм Кана і перевіряємо розмір черги перед кожним вилученням. Як тільки він містить більше однієї вершини, беремо будь-які дві з них як та , і будь-яку іншу вершину графа як . Якщо такий момент так і не настане, виводимо .

Кожна вершина та кожне ребро обробляються не більше одного разу, тому часова складність алгоритму становить O(n + m), а використання пам'яті — O(n + m).

Приклад

Графи з тестових прикладів мають таку структуру:

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

Оголосимо структури даних. Вхідний граф представлений списком суміжності , а вхідні степені вершин зберігаються в масиві InDegree.

vector<vector<int> > g;
vector<int> InDegree;
deque<int> q;

Зчитаємо вхідні дані.

scanf("%d %d", &n, &m);
g.resize(n + 1);
InDegree.resize(n + 1);
for (i = 0; i < m; i++)
{
  scanf("%d %d", &a, &b);
  g[a].push_back(b);

Для кожного ребра збільшуємо InDegree[b] на .

  InDegree[b]++;
}

Усі вершини з нульовою вхідною степенню додаються до черги .

for (i = 1; i < InDegree.size(); i++)
  if (InDegree[i] == 0) q.push_back(i);

Три шукані вершини зберігаються у змінних та .

x = y = -1;

Продовжуємо виконувати алгоритм, доки черга не порожня.

while (!q.empty())
{

Якщо черга одночасно містить більше однієї вершини, це означає, що розв'язок знайдено.

  if (q.size() > 1)
  {
    x = q[0];
    y = q[1];
    break;
  }

Виймаємо вершину з черги — вона стане поточною вершиною в топологічному порядку.

  v = q.front(); q.pop_front();

Видаляємо з графа всі ребра вигляду (v, to). Для кожного такого ребра зменшуємо вхідну степінь вершини . Якщо вхідна степінь стає нульовою, додаємо її до черги.

  for (int to : g[v])
  {
    InDegree[to]--;
    if (InDegree[to] == 0) q.push_back(to);
  }
}

Алгоритм Кана завершено. Якщо значення усе ще , це означає, що розв'язку не існує.

if (x == -1)
  printf("-1\n");
else
{

Якщо розв'язок існує, обираємо як найменшу вершину, відмінну від та .

  z = 1;
  while (z == x || z == y) z++;
    printf("%d %d %d\n", x, y, z);
}