You are on page 1of 9

Phần 1: Tìm kiếm DFS và BFS trên đồ thị.

Vấn đề: Cho một đồ thị G = (V,E) trong đó có 2 đỉnh u và v. 1 đường đi từ u đến v trong đồ thị là dãy (u, x 1,x2, .. , xk, v)
với (xi, xi + 1) là 1 cạnh trong đồ thị. Đỉnh u là đỉnh đầu, đỉnh v là đỉnh cuối của đường đi.
Ví dụ:

A - Đồ thị vô hướng B - Đồ thị có hướng


Trong đồ thị A, đường đi từ 1 đến 5 có thể là 1 – 4 – 5 hoặc 1 – 2 – 3 – 5…
Trong đồ thị B, đường đi từ 1 đến 5 chỉ có thể là 1– 2 – 3 – 5.
Từ đây, ta thấy trong đồ thị tồn tại một bài toán duyệt qua tất cả các đỉnh có thể đến được từ một đỉnh xuất phát nào đó. Đó
là bài toán liệt kê mà yêu cầu của nó là không được bỏ xót hay lặp lại bất kì đỉnh nào. Vì vậy, ta cần những thuật toán cho
phép duyệt một cách hệ thống các đỉnh, điển hình là 2 thuật toán DFS và BFS sẽ được trình bày ở dưới đây.

1 –DFS:
DFS là viết tắt của Depth-First Search (Tìm kiếm theo chiều sâu) là một dạng thuật toán tìm kiếm theo chiều sâu của các
đỉnh trong đồ thị.

Tư tưởng:

Từ một đỉnh nút (gốc) ban đầu :


- Duyệt đi xa nhất theo từng nhánh. Khi nhánh đã duyệt hết, lùi về từng đỉnh để tìm và duyệt những
nhánh tiếp theo
- Quá trình duyệt chỉ dừng lại khi tìm thấy đỉnh cần tìm hoặc tất cả các đỉnh đều đã được duyệt qua.
Từ đây, ta thấy để lùi về từng đỉnh sau khi duyệt hết dễ dàng, ta sẽ sử dụng thuật toán đệ quy.

Bài toán: Cho một đồ thị vô hướng không trọng số, 2 đỉnh S và T. Yêu cầu hãy in ra các điểm có thể đến được từ S và một
đường đi từ S đến T.
Dữ liệu: Nhập từ file DFS.INP:
- Dòng đầu tiên gồm 4 số N là số đỉnh của đồ thị, M là số cạnh của đồ thị, đỉnh S và T (N, M ≤ 100).
- M dòng tiếp theo, mỗi dòng có dạng 2 số nguyên dương u và v thể hiện có đường nối từ 2 đỉnh u và
v.
Kết quả: Ghi ra file DFS.OUT:
- Dòng đầu tiên gồm các đỉnh có thể đến được từ S
- Dòng tiếp theo là 1 đường đi từ S đến T.
Ví dụ:
DFS.INP DFS.OUT
8715 23456
12 1235
23
14
35
36
43
78

Hướng dẫn :
Tư tưởng thuật toán có thể được trình bày như sau : Trước hết, mọi đỉnh u kề với S đều có thể đến được từ S, mọi đỉnh v kề
với u cũng có thể đến được từ S, … Từ đây, ta sử dụng đệ quy như đã nói ở trên với hàm dfs(u) mô tả việc ta đang thăm
đỉnh u và tiếp tục gọi dfs(v) với v là 1 đỉnh kề với u. Để tránh việc thăm 1 đỉnh nhiều lần, ta sẽ đánh dấu đỉnh đã thăm bằng
1 mảng bool.

Mã giả:
void dfs(int u) {
đánh dấu đã thăm đỉnh u;
duyệt các đỉnh v kề với u:
nếu v đã thăm thì continue;
nếu v chưa thăm : dfs(v);
}
Ngoài ra, để giải quyết truy vấn 2, tức là in ra đường đi, ta sẽ tạo mảng trace[v] tức là đỉnh đi được trực tiếp đến v. Ví dụ ,
từ u ta đi đến đỉnh v liền kề thì trace[v] = u.
Khi đó, muốn tìm đường đi từ S đến T sẽ là:
T <- u1 = trace[T] <- u2 = trace[u1] <- … <- S.
Do đó, ta hoàn toàn có thể sử dụng đệ quy để in ra đường đi này.
Ví dụ: ta bắt đầu đi từ đỉnh S là 1.
- Từ đỉnh 1 -> đỉnh 2 -> 3 -> 6.
- Đến đỉnh 6, ta không thể đi tiếp được nên
ta sẽ quay về đỉnh 3, rồi đi xuống đỉnh 5.
- Tương tự, sau khi đến đỉnh 5, ta về lại đỉnh
3, sau đó đi sang đỉnh 4.Từ đỉnh 4, ta không
thể đi đến đỉnh 1 vì ta đã thăm trước đó.
- Sau đó, ta về lại đỉnh 3, đã thăm hết tất cả
các đỉnh kề, ta lại về đỉnh 2 và đỉnh 1.
- Tiếp theo, đỉnh 1 không thể thăm đỉnh 4 vì
đỉnh 4 đã được thăm trước đó.
- Kết thúc DFS từ S.
- Các đỉnh được thăm là 2, 3, 4, 5, 6.

Code mẫu:
Ở đây, ta sẽ sử dụng danh sách kề để lưu các đỉnh kề nhau có cạnh nối trực tiếp.
#include<bits/stdc++.h>
using namespace std;
const int N = 100 + 7;
int n, m, s, t;
vector <int> adj[N];
bool visited[N]; // mảng để đánh dấu đỉnh đã thăm.
int trace[N];
void dfs(int u) { // xử lý truy vấn 1
visited[u] = true; // đánh dấu đỉnh này đã thăm.
for (int i = 0; i < adj[u].size(); i++) {
int v = adj[u][i];
if (visited[v])
continue; // đỉnh đã thăm trước đó thì không thăm lại nữa.
dfs(v);
}
}
void dfs2(int u) { // xử lý truy vấn 2
visited[u] = true;
if (u == t) return; // đã tìm thấy đường đến đỉnh t, ngắt dfs
for (int i = 0; i < adj[u].size(); i++) {
int v = adj[u][i];
if (visited[v]) continue;
trace[v] = u; // gán giá trị cho mảng trace
dfs2(v);
}
}
void in(int x) {
if (x == 0) return; // đã hết phần đường đi
in(trace[x]);
cout << x << " ";
}
void input() { // Nhập dữ liệu
cin >> n >> m >> s >> t;
for (int i = 1; i <= m; i++) {
int u, v;
cin >> u >> v;
adj[u].push_back(v); // lưu các đỉnh bằng danh sách kề.
adj[v].push_back(u);
}
}
void init() { // khởi tạo
for (int i = 1;i <= n; i++) {
visited[i] = 0;
trace[i] = 0;
}
}
int main(){
ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
freopen("dfs.inp", "r", stdin);
freopen("dfs.out", "w", stdout);
input();
init();
dfs(s); // truy vấn 1
for (int i = 1; i <= n; i++)
if (visited[i] && i != s) // đỉnh đã được thăm từ S
cout << i << " ";
cout << '\n';
init();
dfs2(s); // truy vấn 2
if (! visited[t]) { // không thể đi từ S -> T
cout << "Khong ton tai duong di tu " << s << " -> " << t;
}
else
in(t); // sử dụng đệ quy để in đường đi từ S -> T
return 0;
}
Khi biểu diễn theo danh sách kề, ta có độ phức tạp của thuật toán DFS là O(n + m) với n là số đỉnh và m là số cạnh.
Với đồ thị có hướng, ta cũng xử lý tương tự, chỉ khác phần tạo danh sách kề. Ví dụ nếu chỉ có đường đi 1 chiều từ u đến v,
ta chỉ cần 1 dòng: adj[u].push_back(v);
6. Bài tập ứng dụng
6.1. Miền 0 (Nguồn: Đề thi học sinh giỏi tỉnh Thanh Hóa năm 2009)
Cho một hình chữ nhật gồm M hàng, N cột, được chia thành MxN ô vuông. Mỗi ô vuông được ghi
một trong hai số nguyên 0 hoặc 1.
Miền 0 là một miền liên tục các số 0 thuộc các ô chung cạnh với nhau. Diện tích miền là số lượng
các ô vuông cùng giá trị thuộc miền đó.
Yêu cầu: Tính diện tích miền 0 lớn nhất của hình chữ nhật đã cho.
Dữ liệu: Vào từ file văn bản BAI5.INP:
 Dòng đầu tiên ghi hai số M, N.
 M dòng tiếp theo, mỗi dòng ghi N số lần lượt là giá trị các ô trong bảng số.
Kết quả: Ghi ra file văn bản BAI5.OUT số nguyên duy nhất là diện tích miền 0 lớn nhất.
Giới hạn: 1 < M, N < 100.
Ví dụ:
BAI5.INP BAI5.OUT
34 4
0001
1101
0010
6.2. Bãi cỏ
Bessie dự định cả ngày sẽ nhai cỏ xuân và ngắm nhìn cảnh xuân trên cánh đồng của nông dân John,
cánh đồng này được chia thành các ô vuông nhỏ với R (1 <= R <= 100) hàng và C (1 <= C <= 100) cột.
Bessie ước gì có thể đếm được số bãi cỏ trên cánh đồng.
Mỗi bãi cỏ trên cánh đồng là các ô chứa ký tự ‘#‘ nằm kề nhau theo cạnh. Cho bản đồ của cánh đồng,
hãy nói cho Bessie biết có bao nhiêu bãi cỏ trên cánh đồng.
Dữ liệu: vào từ file văn bản VBGRASS.INP
 Dòng đầu: 2 số nguyên cách nhau bởi dấu cách: R và C
 Dòng 2..R+1: Dòng i+1 mô tả hàng i của cánh đồng với C ký tự, các ký tự là ‘#’ hoặc ‘.’ .

Kết quả: Ghi ra file VBGRASS.OUT một số nguyên cho biết số lượng bãi cỏ trên cánh đồng.
Ví dụ:
VBGRASS.INP VBGRASS.OUT
56 5
.#....
..#...
..#..#
...##.
.#....
6.3. Nguồn: http://vn.spoj.com/problems/MESSAGE/
Một lớp gồm N học sinh, mỗi học sinh cho biết những bạn mà học sinh đó có thể liên lạc được (chú
ý liên lạc này là liên lạc một chiều : u có thể gửi tin tới v nhưng v thì chưa chắc đã có thể gửi tin tới u).
Thầy chủ nhiệm đang có một thông tin rất quan trọng cần thông báo tới tất cả các học sinh. Để tiết
kiệm thời gian, thầy chỉ nhắn tin tới 1 số học sinh rồi sau đó nhờ các học sinh này nhắn lại cho tất cả các
bạn mà các học sinh đó có thể liên lạc được, và cứ lần lượt như thế làm sao cho tất cả các học sinh trong
lớp đều nhận được tin .
Yêu cầu: Hãy tìm một số ít nhất các học sinh mà thầy chủ nhiệm cần nhắn.
Dữ liệu: Vào từ file MESSAGE.INP gồm
- Dòng đầu là N, M (N <= 800, M là số lượng liên lạc 1 chiều)
- Một số dòng tiếp theo mỗi dòng gồm 2 số u , v cho biết học sinh u có thể gửi tin tới học sinh v
Kết quả: Ghi ra file MESSAGE.OUT gồm 1 dòng ghi số học sinh cần thầy nhắn tin.
Ví dụ:
MESSAGE.INP MESSAGE.OUT
12 15 2
13
36
61
68
8 12
12 9
96
24
45
52
46
7 10
10 11
11 7
10 9
6.4. BẢO TỒN ĐỘNG VẬT HOANG DÃ
Một khu bảo tồn động vật có n địa điểm và các đường đi hai chiều nối các địa điểm đó, địa điểm thứ
i có nhiệt độ là ti, giữa hai địa điểm bất kỳ có nhiều nhất là một đường đi nối chúng.
Người ta muốn di chuyển một loài động vật quý hiếm từ địa điểm A tới địa điểm B, tuy nhiên nếu
chênh lệch về nhiệt độ giữa hai địa điểm liên tiếp trên đường đi là quá cao thì loài động vật này rất có thể
bị chết.
Yêu cầu: Hãy chỉ ra một hành trình mà độ lệch nhiệt độ lớn nhất giữa hai địa điểm liên tiếp bất kỳ trên
đường đi là cực tiểu.
Dữ liệu: Vào từ file văn bản MOVE.INP
 Dòng 1: Chứa ba số n, A, B (2  n  200; A  B)
 Dòng 2: Chứa n số tự nhiên t1, t2, ..., tn (i: 0  ti  2.106)
 Các dòng tiếp theo, mỗi dòng chứa hai số nguyên dương u, v cho biết giữa hai địa điểm u và v có đường
đi nối chúng.
Kết quả: Ghi ra file văn bản MOVE.OUT là độ lệch nhiệt độ lớn nhất giữa hai địa điểm liên tiếp bất kỳ
trên đường đi tìm được, nếu không tồn tại đường đi thì dòng này ghi số -1.
Các số trên một dòng của Input/ Output file được ghi cách nhau ít nhất một dấu cách.
Ví dụ:
MOVE.INP MOVE.OUT
714 2 22 24
2 5
20 22 29 30 24 27 26
12 30
13 20 1 4 7 26

14
24 3 6
25 29 27

34
36
45
46
57
67

Câu 1: DAINHAT.CPP
Thành phố VanhG được xem như một đồ thị có hướng gồm N khu du lịch và M con đường một chiều nối từ khu du lịch u
đến khu du lịch v. Trong lúc quy hoạch, chủ tịch thành phố đã đảm bảo rằng đồ thị này không tồn tại chu trình (tức là
không tồn tại một đường đi đóng kín). Vinci dự định kì nghỉ hè sẽ đến đây chơi, và là một người chu đáo, anh muốn tính
xem đoạn đường có hướng dài nhất trong thành phố VanhG có độ dài là bao nhiêu. Quy ước rằng: độ dài của một đường
đi có hướng là số cạnh nằm trên đường đi đó.
Dữ liệu: Vào từ file DAINHAT.INP
+ Dòng đầu tiên chứa hai số nguyên N và M, lần lượt là số đỉnh và số cạnh của đồ thị đã cho. (2 ≤ N ≤ 105 ,1 ≤ M ≤ 105 )
+ Dòng thứ i trong số M dòng tiếp theo, mỗi dòng chứa hai số nguyên xi và yi, biểu diễn cho cạnh thứ i của đồ thị.
Đảm bảo rằng tất cả các cạnh (xi,yi) bất kì đều phân biệt và đồ thị G không có chu trình.
Kết quả: Ghi ra file DAINHAT.OUT là 1 số thể hiện độ dài đường đi dài nhất.
Ví dụ:

DAINHAT.INP DAINHAT.OUT
45 3
12
13
32
24
34
Giải thích:
Đường đi có hướng dài nhất là 1 –3 – 2 – 4 có độ dài là 3.

Câu 2: COVID19.CPP
Cơ quan VSAFE có n nhân viên được đánh số thứ tự từ 1 đến n. Mỗi người có một phòng làm việc riêng của mình. Do nhu
cầu công việc, hàng ngày mỗi nhân viên có thể phải tiếp xúc với một số nhân viên khác ở nhiều phòng khác nhau. Vào một
ngày làm việc bình thường, có một nhân viên bị nhiễm Covid19, tức là F0. Nhưng do không biết nên người này vẫn đi làm.
Đến cuối ngày làm việc, ban y tế kiểm tra mới phát hiện ra người F0 đó. Khả năng lây lan của Covid19 rất nhanh chóng,
một người nhiễm bệnh nếu tiếp xúc với một người khác có thể sẽ truyền bệnh cho người này.
Yêu cầu: Hãy giúp ban y tế kiểm tra xem cuối ngày hôm đó, có tối đa bao nhiêu người có thể là F1, F2, …. để dễ xử lý và
cách ly.
Dữ liệu: nhập vào từ file COVID19.INP
+ Dòng đầu tiên ghi 2 số tự nhiên n,k (1≤ n ≤1000 , 1 ≤ k ≤n) tương ứng là số lượng người làm việc trong toà nhà và số
hiệu của nhân viên là F0.
+ Dòng thứ i trong n dòng tiếp theo ghi danh sách những người có tiếp xúc với người thứ i theo cách sau: số đầu tiên m của
dòng là tổng số nhân viên đã gặp người thứ i, tiếp theo là m số tự nhiên lần lượt là số hiệu của các nhân viên đó.
Nếu m=0 có nghĩa rằng không ai đã tiếp xúc với người thứ i.
Dữ liệu được cho đảm bảo tổng số lần tiếp xúc của tất cả nhân viên trong cơ quan không vượt quá 10 6.
Kết quả: xuất ra file COVID19.OUT
+ Dòng thứ nhất là số lượng người bị F1
+ Dòng thứ 2 là số lượng người là F2
+…
+ Dòng cuối cùng là số người không thuộc dạng F nào cả.
Ví dụ:
COVID19.INP COVID19.OUT
51 2
223 2
213
212
15
14
Giải thích:
- F1 là người số 2, 3; Người số 4, 5 không thuộc dạng F nào cả.

Câu 3: MANGMT.CPP
Mạng máy tính nơi VanhG làm việc gồm có N máy tính, khi nhận được dữ liệu từ máy chủ hay máy khác, máy thứ i sẽ
truyền dữ liệu nó nhận được tới một máy khác có giá trị là A[i] (Có thể tự nối đến chính nó). Nhận nhiệm vụ truyền dữ
liệu từ máy chủ tới N máy tính kia, VanhG nhận ra rằng, nếu anh ấy truyền dữ liệu từ máy chủ tới một máy tính bất kì, thì
có thể một số máy tính sẽ không nhận được dữ liệu. Bởi vậy, VanhG muốn tăng thêm một số ít nhất các liên kết truyền dữ
liệu giữa các máy, để dữ liệu từ máy chủ tới một máy tính bất kì, thì tất cả các máy tính còn lại đều sẽ nhận được dữ liệu.
Hãy giúp VanhG xác định một lượng ít nhất các liên kết cần phải thêm đó.
Chú ý: Nếu máy u có thể truyền dữ liệu tới máy v thì máy v cũng có thể truyền dữ liệu tới máy u vì đây là liên kết 2 chiều.
Dữ liệu: Nhập từ file MANGMT.INP:
+ Dòng thứ nhất gồm số N là số máy tính. (N <= 100000).
+ Dòng tiếp theo gồm N số A[i].
Kết quả: Ghi ra file MANGMT.OUT:
+ Dòng duy nhất là số liên kết cần thêm.
Ví dụ:
MANGMT.INP MANGMT.OUT
3 1
113
Giải thích: Ta cần 1 liên kết giữa 1 và 3 hoặc 2 và 3.

Câu 4: PTFIELD.CPP
Nông trại của bác Vinci có rất nhiều ngọn đồi núi, để bảo vệ khỏi những tên cướp, bác muốn thuê những người canh gác
trên các ngọn đồi này. Để có thể bao quát được tất cả nông trại, những người canh gác phải đứng trên tất cả các đỉnh đồi.
Biết rằng nông trại là một ma trận gồm N (1 < N ≤ 700) hàng và M (1 < M ≤ 700) cột. Mỗi phần tử của ma trận là độ
cao Hij so với mặt nước biển (0 ≤ Hij ≤ 100) của ô (i, j). Hãy giúp bác Vinci xác định số lượng lính gác từ bản đồ.
Đỉnh đồi là 1 hoặc nhiều ô nằm kề nhau của ma trận có cùng độ cao được bao quanh bởi cạnh của bản đồ hoặc bởi các ô có
độ cao nhỏ hơn. Hai ô gọi là kề nhau nếu độ chênh lệch giữa tọa độ X không quá 1 và chênh lệch tọa độ Y không quá 1.
Dữ liệu: Đọc từ file PTFIELD.INP:
+ Dòng 1: Hai số nguyên N và M.
+ Dòng tiếp theo là ma trận N * M.
Kết quả: Ghi vào file PTFIELD.OUT:
+ Dòng duy nhất là số lính gác.
Ví dụ:
PTFIELD.INP PTFIELD.OUT
87 3
4322101
3332101
2222100
2111100
1100010
0001110
0122110
0111210

Giải thích: Các ô tô đậm là những vùng trong các đỉnh đồi.

Câu 5: VKINGDOM.CPP
Vinci rất thích những cuốn tiểu thuyết về những vương quốc hay những thế giới mở. Trong một hôm, anh mơ mình lạc vào
một vương quốc tên là Peaceful với những sinh vật kì bí đầy màu sắc. Vương quốc có cấu trúc như một cây với N thành
phố và N – 1 con đường hai chiều, từ một thành phố bất kì Vinci có thể đi đến những thành phố khác bằng cách băng qua
một vài con đường. Đặc biệt, thành phố được đánh số từ 1 đến N với 1 là thủ đô của vương quốc.
Quốc vương đang đau đầu vì phải chọn ra K thành phố để phát triển kinh tế, trong khi N - K thành phố còn lại để phát triển
du lịch. Đặc biệt, hàng năm mỗi thành phố kinh tế sẽ phải cử thị trưởng về thành phố thủ đô để báo cáo tình hình tài chính
bằng cách đi qua đoạn đường ngắn nhất từ thành phố đó về thủ đô.
Mỗi khi đi qua 1 thành phố du lịch, thị trưởng sẽ được tăng thêm độ hạnh phúc của mình lên 1. Vì rất yêu quý người dân,
quốc vương muốn tổng độ hạnh phúc của tất cả thị trưởng đến thủ đô là lớn nhất có thể. Vinci rất muốn giúp Quốc vương
nên muốn nhờ các bạn lập trình giải bài toán này.
Dữ liệu: Nhập từ file VKINGDOM.INP:
+ Dòng đầu tiên gồm 2 số N và K (2 ≤ N ≤ 2*105, 1 ≤ K ≤ N).
+ Tiếp theo gồm n – 1 dòng, mỗi dòng gồm 2 số u và v thể hiện đường đi 2 chiều từ thành phố u đến v.
Kết quả: Ghi ra file VKINGDOM.OUT:
+ 1 dòng duy nhất là tổng độ hạnh phúc lớn nhất của các thị trưởng đến thủ đô.
Ví dụ:
VKINGDOM.INP VKINGDOM.OUT
41 2
12
13
24
Giải thích:
Thành phố kinh tế là 4.
Khi đi lên thành phố 1 thì sẽ đi qua 2 thành phố
du lịch là 2 và 1.
Vậy tổng độ hạnh phúc là 2.

You might also like