Топологічне сортування
Дано орієнтований граф з вершинами та ребрами. Потрібно пронумерувати його вершини таким чином, аби кожне ребро вело з вершини з меншим номером у вершину з більшим.
Іншими словами, потрібно знайти перестановку вершин (топологічний порядок), відповідну порядку, що задається усіма ребрами у графі.
Топологічне сортування може бути не унікальним (наприклад, коли є три такі вершини , , , що з є шлях в та в , але ні з в , ні з в добратися не можна).
Топологічне сортування може не існувати зовсім - якщо граф містить цикли. Оскільки тоді виникає протиріччя: є шлях і з однієї вершини в іншу, і навпаки.
Алгоритм
Для розв'язку скористаємось пошуком в глибину.
Припустимо, що граф ациклічний, тобто розв'язок існує. Що робить пошук в глибину? При запуску з якоїсь вершини він намагається запуститися уздовж всіх ребер, вихідних з . Уздовж тих ребер, кінці яких вже були відвідані раніше, він не проходить, а уздовж всіх інших - проходить і викликає себе від їх кінців.
Таким чином, на момент виходу з виклику всі вершини, досяжні з як безпосередньо (по ребру), так і дотично (по деякому шляху), вже відвідані пошуком в глибину. Отже, якщо ми будемо в момент виходу з додавати нашу вершину в початок деякого списку, то у кінці алгоритму в цьому списку отримаємо топологічне сортування.
Пояснити можна також за допомогою поняття "часу виходу" пошуку в глибину. Час виходу для кожної вершини - це момент часу, в який закінчив працювати виклик пошуку в глибину. Часи виходу можна пронумерувати від до . Легко зрозуміти, що при пошуці в глибину час виходу з будь-якої вершини завжди більший, ніж час виходу з усіх вершин, досяжних з неї (тобто вони були відвідані або до виклику , або під час нього). Таким чином, шукане топологічне сортування - це сортування у порядку зменшення часів виходу.
Складність алгоритму така ж як і у пошуку в глибину - , де - кількість вершин, - кількість ребер.
Реалізація
Наведемо реалізацію, що припускає, що граф ациклічний, тобто шукане топологічне сортування існує. При необхідності перевірку графа на ациклічність можна здійснити додатковим пошуком в глибину, як описано у статті про пошук циклу.
int n; // кількість вершин
vector<int> g[MAXN]; // граф, список суміжності
bool visited[MAXN];
vector<int> ans;
void dfs(int v) {
visited[v] = true;
for (int i = 0; i < g[v].size(); i++) {
int to = g[v][i];
if (!visited[to]) {
dfs(to);
}
}
ans.push_back(v);
}
void topological_sort() {
for (int i = 0; i < n; i++) {
visited[i] = false;
}
ans.clear();
for (int i = 0; i < n; i++) {
if (!visited[i]) {
dfs(i);
}
}
reverse(ans.begin(), ans.end());
}
Тут константа MAXN
задає максимально можливу кількість вершин у графі.
Основна функція розв'язку - це topological_sort
. Вона ініціалізує допоміжний масив visited
пошуку в глибину, запускає його, і у кінці масив ans
містить шукане топологічне сортування.
Застосування
Є змінних, значення яких нам невідомі. Відомо лише про деякі пари змінних, у яких одна змінна менша іншої. Потрібно перевірити чи не суперечливі ці нерівності, і якщо ні, видати змінні у порядку їх зростання (якщо розв'язків декілька - видати будь-який).
Потрібно створити граф з вершинами, де ребра ведуть з меншої змінної у більшу. Провести перевірку на ациклічність. Якщо граф містить цикл, то нерівності суперечливі, а якщо не містить циклу, то вершини у топологічному порядку відповідатимуть змінним у порядку їх зростання.
Задачі
e-olymp - 1948 - Топологічне сортування | Розв'язки: C++
Spoj - TOPOSORT - Topological Sorting | Розв'язки: C++
Spoj - RPLA - Answer the boss! | Розв'язки: C++
Online Judge - 124 - Following Orders | Розв'язки: Go
Online Judge - 200 - Rare Order | Розв'язки: Go
Online Judge - 10305 - Ordering Tasks | Розв'язки: Go, Python