У задачі потрібне знайти мінімальну множину вершин графа, досяжну з усіх інших вершин. Для цього розглянемо конденсацію графа. Звернемо увагу на компоненти сильної зв'язності, з яких не виходить жодного ребра.
Якщо в такій компоненті не збудувати пожежну станцію, то пожежна машина, що приїхала з іншої компоненти, зможе загасити пожежу, однак повернутися назад, слідуючи напрямкам доріг, вже не зможе. Згідно з цим, у кожній компоненті сильної зв'язності без вихідних ребер необхідно побудувати хоча б одну пожежну станцію.
Будувати додаткові станції не потрібно, оскільки з будь-якої вершини графа існує шлях по ребрах в одну з компонент без вихідних ребер.
Приклад
Вхідний граф містить три компоненти сильної зв'язності. З компонент, що складаються з однієї вершини ( і ), не виходить жодного ребра. Згідно, достатньо побудувати пожежні станції саме в цих компонентах.
Реалізація алгоритму
Вхідний граф зберігаємо у списку суміжності . Оборотний граф (граф, в якому напрямки всіх ребер інвертовані) зберігаємо у списку суміжності .
vector<vector<int> > g, gr;
vector<int> used, top, color, repr;Функція dfs1 реалізує пошук у глибину на вхідному графі. У масиві зберігаємо порядок вершин, що відповідає моменту завершення їх обробки при обході в глибину.
void dfs1(int v)
{
used[v] = 1;
for (int to : g[v])
if (!used[to]) dfs1(to);
top.push_back(v);
}Функція dfs2 реалізує пошук у глибину на оборотному графі. Всі вершини, відвідані в результаті рекурсивних викликів dfs2, належать одній компоненті сильної зв'язності. Всі такі вершини фарбуються кольором . Вершини, що належать одній компоненті сильної зв'язності, мають однаковий колір.
Для кожного кольору в масиві зберігається представник відповідної компоненти — будь-який номер вершини, пофарбованої цим кольором.
void dfs2(int v, int c)
{
color[v] = c;
repr[c] = v;
for (int to : gr[v])
if (color[to] == -1) dfs2(to, c);
}Основна частина програми. Читаємо вхідні дані. Будуємо оборотний граф.
scanf("%d %d", &n, &m);
g.resize(n + 1);
gr.resize(n + 1);
for (i = 0; i < m; i++)
{
scanf("%d %d", &a, &b);
g[a].push_back(b);
gr[b].push_back(a);
}Запускаємо пошук у глибину на вихідному графі. Порядок, у якому завершується обробка вершин, зберігаємо у масиві .
used.resize(n+1);
for (i = 1; i <= n; i++)
if (!used[i]) dfs1(i);Запускаємо пошук у глибину на оборотному графі. Вершини оборотного графа оброблюються у порядку, заданому у масиві , при цьому обход виконується справа наліво — від кінця масиву до початку. Для зручності подальшої обробки масива попередньо розгортаємо.
color.assign(n+1, -1);
repr.assign(n+1, -1);
reverse(top.begin(), top.end());Вершини, що належать одній компоненті сильної зв'язності, фарбуються одним і тим же кольором. Поточний колір зберігається в змінній .
c = 0;
for (int v : top)
if (color[v] == -1) dfs2(v, c++);Змінна містить кількість компонент сильної зв'язності.
used.assign(c, 1);
for (i = 1; i < g.size(); i++)
for (int to : g[i])Перебираємо всі ребра графа і перевіряємо, чи належать вершини та різним компонентам сильної зв'язності. Це визначається за різними кольорами вершин. Якщо вершини знаходяться в різних компонентах, у компоненті з кольором color[i] немає необхідності будувати пожежну станцію, тому встановлюємо used[color[i]] = 0.
if (color[i] != color[to]) used[color[i]] = 0;
}Підрахуємо кількість компонент , в яких слід побудувати пожежну станцію.
c = 0;
for (i = 0; i < used.size(); i++)
if (used[i]) c++;Виводимо мінімальне необхідне число пожежних станцій.
printf("%d\n",c);Для кожної компоненти з кольором , з якої не виходить жодного ребра, виводимо її представника (значення repr[i]). Саме ці номери будинків вказують, біля яких слід побудувати пожежні станції.
for (i = 0; i < used.size(); i++)
if (used[i]) printf("%d ",repr[i]);
printf("\n");