Сначала сформулируем общую схему алгоритмов этого типа. И без обычных для учебников избыточных сложностей в виде белых-серых-черных вершин.
- Заводим PLAN поиска — контейнер данных, где будем хранить вершины в которых мы планируем побывать. Изначально он пуст.
- Добавляем в PLAN поиска исходную вершину с которой нам предписано начать.
- Пока PLAN не пуст и цель поиска не достигнута делаем следующее
- GET: Извлекаем из PLAN какую-нибудь вершину v.
- Посещаем вершину v. Если мы не просто обходим вершины, а что-то ищем, то здесь самое время обыскать вершину v на предмет достижения цели поиска.
- Как-то отмечаем, что вершина v уже посещена.
- PUT: Добавляем в PLAN все соседние с v вершины, которые еще не были посещены.
- Выводим результат поиска.
Самым важным для реализации этой схемы является PLAN. Это контейнер данных в котором нам нужны две функции GET — чтобы что-то из контейнера достать и PUT — чтобы в контейнер что-то положить. Конечно лучше использовать уже готовые контейнеры. Выбор контейнера будет определять стратегию поиска.
DFS (Depth-first Search). Например, если в качестве контейнера выбрать СТЕК, то мы реализуем алгоритм поиска в глубину. Ведь организация доступа к элементам стека такова, что мы в первую очередь будем посещать те вершины, которые попали в план последними. Посмотрим на код решения задачи
Обход в глубину:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
#include <iostream> #include <stack> using namespace std; int main() { // чтение исходных данных int n, v; cin >> n >> v; int matrix[n][n]; for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) cin >> matrix[i][j]; stack <int> plan; // план посещения в виде стека plan.push(--v); // мы нумеруем с 0, а не с 1 matrix[v][v] = 1; // отмечаем, что эта вершина уже заносилась в план int counter = 1; // начальную уже сосчитали while (!plan.empty()) { v = plan.top(); // посещаем следующую по плану вершину plan.pop(); // удаляем ее из плана посещения for (int u = 0; u < n; u++) { // перебираем соседние с ней if (matrix[v][u] and !matrix[u][u]) { // если новая, то plan.push(u); // добавляем ее в план matrix[u][u] = 1; // отмечаем, что уже не новая counter++; // считаем, сколько было вершин } } } cout << counter << endl; } |
Единственное важное пояснение. Чтобы отметить, что вершина уже посещалась, я использую диагональ матрицы смежности графа. в условии специально подчеркнули, что там всегда нули, а значит я могу поставить matrix[v][v] = 1, чтобы обозначить вершину v как уже посещенную.
BFS (Breadth-first Search). Стоит нам немного изменить код и использовать для хранения плана ОЧЕРЕДЬ, алгоритм меняет стратегию и осуществляет поиск в ширину. Поскольку вершины будут посещаться в том порядке в котором мы их добавляли, это очень похоже на распространение волны из начальной точки. Отсюда другое название таких алгоритмов — заливки (flood) или волновые алгоритмы.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
#include <iostream> #include <queue> using namespace std; int main() { // чтение исходных данных int n, v; cin >> n >> v; int matrix[n][n]; for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) cin >> matrix[i][j]; queue <int> plan; // план посещения в виде очереди plan.push(--v); // мы нумеруем с 0, а не с 1 matrix[v][v] = 1; // отмечаем, что эта вершина уже заносилась в план int counter = 1; // начальную уже сосчитали while (!plan.empty()) { v = plan.front(); // посещаем следующую по плану вершину plan.pop(); // удаляем ее из плана посещения for (int u = 0; u < n; u++) { // перебираем соседние с ней if (matrix[v][u] and !matrix[u][u]) { // если новая, то plan.push(u); // добавляем ее в план matrix[u][u] = 1; // отмечаем, что уже не новая counter++; // считаем, сколько было вершин } } } cout << counter << endl; } |
Если для хранения плана написать свой контейнер или хотя бы переопределить методы GET и PUT, то вы получите новый алгоритм поиска. Например, можно извлекать вершину из плана случайным образом. В этом случае мы получим один из рандомизированных алгоритмов семейства Монте-Карло.
Задание: Найдите все четыре места, где код поиска в глубину отличается от кода поиска в ширину.
Подсказка: Если не смогди найти четвертое отличие — оно в комментариях 🙂
Для отправки комментария необходимо войти на сайт.