DFS деген не?
DFS — графты тереңдік бойынша обход жасау алгоритмі. Толық аты: Depth-First Search.
DFS мүмкін болғанша графтың ішіне терең кіріп кетеді. Егер ары қарай баратын жер болмаса, артқа қайтады.
бірінші бармаған көршіге өттік
тағы тереңірек кеттік
көрші қалмаса, артқа қайтамыз
DFS негізгі идеясы
Бізде граф бар:
vector<int> g[N];
Және visited массиві бар:
bool used[N];
Егер біз v вершинасына кірсек, оны visited қыламыз:
used[v] = true;
Кейін барлық көршілерін қараймыз:
for (int to : g[v]) {
if (!used[to]) {
dfs(to);
}
}
Егер көршіге әлі бармасақ, сол көршіден DFS бастаймыз.
Обход мысалы
Мына граф болсын:
| |
3 -- 5
DFS-ті 1 вершинасынан бастаймыз.
2-ге барамыз
4-ке барамыз
2-ге қайта ораламыз
5-ке барамыз
3-ке барамыз
DFS тәртібі әртүрлі болуы мүмкін. Ол adjacency list ішіндегі көршілердің ретіне байланысты.
Обычный DFS
void dfs(int v) {
used[v] = true;
for (int to : g[v]) {
if (!used[to]) {
dfs(to);
}
}
}
Мұнда:
- v — қазіргі вершина
- used[v] — осы вершинаға кірдік пе, жоқ па
- to — v вершинасының көршісі
- dfs(to) — тереңірек кіру
Задача 1: жетуге бола ма?
Неориентированный граф берілген. s вершинасынан t вершинасына жетуге бола ма, соны тексеру керек.
Input
5 4 1 5
1 2
2 3
3 4
4 5
Output
YES
DFS толық реализация
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int n, m, s, t;
vector<int> g[N];
bool used[N];
void dfs(int v) {
used[v] = true;
for (int to : g[v]) {
if (!used[to]) {
dfs(to);
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m >> s >> t;
for (int i = 1; i <= m; i++) {
int a, b;
cin >> a >> b;
g[a].push_back(b);
g[b].push_back(a);
}
dfs(s);
if (used[t]) {
cout << "YES";
} else {
cout << "NO";
}
return 0;
}
Компоненттер
Компонента связности — графтың бір бөлігі. Сол бөліктегі кез келген вершинадан басқа вершинаға жетуге болады.
DFS арқылы компонент санын оңай табуға болады.
егер вершина visited емес болса
онда жаңа компонента таптық
сол вершинадан dfs бастаймыз
int cnt = 0;
for (int i = 1; i <= n; i++) {
if (!used[i]) {
cnt++;
dfs(i);
}
}
Задача 2: компонент саны
Неориентированный граф берілген. Компоненттер санын табу керек.
Input
6 3
1 2
2 3
5 6
Output
3
Компоненттер:
4
5 6
Компоненттерді санау коды
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int n, m;
vector<int> g[N];
bool used[N];
void dfs(int v) {
used[v] = true;
for (int to : g[v]) {
if (!used[to]) {
dfs(to);
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m;
for (int i = 1; i <= m; i++) {
int a, b;
cin >> a >> b;
g[a].push_back(b);
g[b].push_back(a);
}
int cnt = 0;
for (int i = 1; i <= n; i++) {
if (!used[i]) {
cnt++;
dfs(i);
}
}
cout << cnt;
return 0;
}
Барлық компоненттерді шығару
Біз компонент санын ғана емес, әр компоненттің вершинасын да шығара аламыз.
vector<int> component;
void dfs(int v) {
used[v] = true;
component.push_back(v);
for (int to : g[v]) {
if (!used[to]) {
dfs(to);
}
}
}
Әр жаңа DFS алдында component массивін тазалаймыз:
component.clear();
dfs(i);
Компоненттерді шығару коды
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int n, m;
vector<int> g[N];
bool used[N];
vector<int> component;
void dfs(int v) {
used[v] = true;
component.push_back(v);
for (int to : g[v]) {
if (!used[to]) {
dfs(to);
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m;
for (int i = 1; i <= m; i++) {
int a, b;
cin >> a >> b;
g[a].push_back(b);
g[b].push_back(a);
}
for (int i = 1; i <= n; i++) {
if (!used[i]) {
component.clear();
dfs(i);
for (int x : component) {
cout << x << ' ';
}
cout << '\n';
}
}
return 0;
}
DFS және цикл
DFS арқылы неориентированный графта цикл бар ма, соны тексеруге болады.
Идея:
егер to visited болса
және to v-ның parent-і болмаса
онда цикл бар
bool hasCycle = false;
void dfs(int v, int p) {
used[v] = true;
for (int to : g[v]) {
if (to == p) {
continue;
}
if (used[to]) {
hasCycle = true;
continue;
}
dfs(to, v);
}
}
Цикл табу толық коды
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int n, m;
vector<int> g[N];
bool used[N];
bool hasCycle = false;
void dfs(int v, int p) {
used[v] = true;
for (int to : g[v]) {
if (to == p) {
continue;
}
if (used[to]) {
hasCycle = true;
continue;
}
dfs(to, v);
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m;
for (int i = 1; i <= m; i++) {
int a, b;
cin >> a >> b;
g[a].push_back(b);
g[b].push_back(a);
}
for (int i = 1; i <= n; i++) {
if (!used[i]) {
dfs(i, -1);
}
}
if (hasCycle) {
cout << "YES";
} else {
cout << "NO";
}
return 0;
}
DFS матрицада
DFS тек графта емес, матрицада да қолданылады. Мысалы, картадағы областтар санын табу үшін.
Мысалы:
- . — бос клетка, жүруге болады
- # — стена, жүруге болмайды
Бір клеткадан 4 жаққа жүреміз:
int dx[4] = {-1, 1, 0, 0};
int dy[4] = {0, 0, -1, 1};
Задача 3: матрицадағы областтар саны
Матрица берілген. . символдарынан тұратын областтар санын табу керек.
Input
4 5
..#..
..#..
####.
...#.
Output
3
Матрицадағы DFS толық коды
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
int n, m;
char a[N][N];
bool used[N][N];
int dx[4] = {-1, 1, 0, 0};
int dy[4] = {0, 0, -1, 1};
void dfs(int x, int y) {
used[x][y] = true;
for (int k = 0; k < 4; k++) {
int nx = x + dx[k];
int ny = y + dy[k];
if (nx < 1 || nx > n || ny < 1 || ny > m) {
continue;
}
if (a[nx][ny] == '#') {
continue;
}
if (used[nx][ny]) {
continue;
}
dfs(nx, ny);
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> a[i][j];
}
}
int cnt = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (a[i][j] == '.' && !used[i][j]) {
cnt++;
dfs(i, j);
}
}
}
cout << cnt;
return 0;
}
DFS қашан қолданылады?
- Графтың барлық вершинасын обход жасау керек болса
- Бір вершинадан басқа вершинаға жетуге бола ма тексеру керек болса
- Компонента связности табу керек болса
- Графта цикл бар ма тексеру керек болса
- Деревомен жұмыс істеу керек болса
- Матрицадағы клеткаларды обход жасау керек болса
- Топологиялық сортировка керек болса
DFS vs BFS
| Алгоритм | Қалай жүреді | Қандай структура | Қайда ыңғайлы |
|---|---|---|---|
| DFS | тереңдікке кіреді | recursion / stack | компоненттер, дерево, цикл |
| BFS | слой бойынша жүреді | queue | невзвешенный графта shortest path |
Асимптотика
| Жағдай | Уақыт | Память |
|---|---|---|
| Граф | O(n + m) | O(n + m) |
| Матрица n × m | O(n · m) | O(n · m) |