Cho đến các vấn đề khó như kiểm tra đồ thị hoàn hảo, Chu trình Hamiliton,… 1.2 Bài toán nghiên cứu Trong báo cáo này c a chúng tôi, chúng tôi s x lý mủ ẽ ử ột bài toán liên quan đến đồ ị
Trang 1TRƯỜNG ĐẠI HỌC PHENIKAA
KHOA CÔNG NGHỆ THÔNG TIN
Hoàng Văn Phú 21010654 21010654@st.phenikaa-uni.edu.vn
Nguy n Th ễ ế Toại 21012898 21012898@st.phenikaa-uni.edu.vn
Giảng viên hướng dẫn: Nguyễn Văn Duy
2024
Trang 2Mục l c ụ
CHƯƠNG 1 Mở đầu 4
1.1 Đặt vấn đề 4
1.2 Bài toán nghiên c u 4ứ CHƯƠNG 2 Nội dung 6
2.1 Cơ sở lý thuy t 6ế Lý thuyết đồ thị 6
Lý thuyết duy ệt đồ thị 6
2.2 Phương pháp giải bài toán 6
Phân tích bài toán 6
Phương pháp giải bài toán 7
Ví dụ minh h a 9ọ 2.3 Xây dựng mã ngu n 10ồ Khai báo các tham số và nhiệm vụ 14
Các hàm được sử dụng trong chương trình và ý nghĩa 14
Kết quả 17
CHƯƠNG 3 Kết luận 18
Trang 3Danh m c t ụ ừ viế ắt t t
Từ viết t t ắ Ý nghĩa
DFS Depth-First Search BFS Breath-First Search
B ng k ả ế hoạch phân công và đóng góp của các thành viên
Vai trò
Hoàng Văn
Trưởng nhóm Thành viên
Milestone Nội dung công việc
I
Tìm kiếm tài liệu, template 30% 70%
Toàn bộ dự
Trang 4CHƯƠNG 1 Mở đầu
1.1 Đặt vấn đề
Trong lập trình, đồ thị là m t d ng c u trúc dộ ạ ấ ữ liệu m nh m và có tính ng ạ ẽ ứ
d ng vào th c t ụ ự ế cao Nguyên nhân đồ thị được s d ng rử ụ ộng rãi vì đồ thị s h u nhi u ở ữ ề
ưu điểm và ứng dụng nổi bật, tiêu biểu là có thể sử dụng đồ thị để mô hình hóa các
m i quan h phố ệ ức tạp giữa các đối tượng trong th c tự ế như: Mạng xã h i, m ng máy ộ ạ tính, giao thông m t cách tr c quan, hi u qu Ngoài ra, s dộ ự ệ ả ử ụng đồ thị còn mang đến
s tự ối ưu hóa hiệu su t nhấ ờ có độ ph c t p th i gian và không gian t t, cho phép x ứ ạ ờ ố ử
lý hi u qu các t p dệ ả ậ ữ liệ ớu l n, hay d dàng tìm ki m, duy t, s a, xóa mà không nh ễ ế ệ ử ả hưởng đến cấu trúc của đồ thị
Ngày nay, có r t nhi u bài toán gi i thuấ ề ả ật ứng dụng đồ thị để giải quy t các v n ế ấ
đề, các vấn đề ừ dễ như: Đếm s thành phần liên thông, Tìm đường đi giữa hai đ nh, t ố ỉ Duyệt đồ thị Cho đến các vấn đề khó như kiểm tra đồ thị hoàn hảo, Chu trình Hamiliton,…
1.2 Bài toán nghiên cứu
Trong báo cáo này c a chúng tôi, chúng tôi s x lý mủ ẽ ử ột bài toán liên quan đến
đồ ị th có tính ng dứ ụng khá cao Đó là bài toán Cảnh sát, còn được biết đến với tên là NKPOLICE, POLICIJA Đề bài của bài toán như sau:
Để truy bắt t i phạm, cảnh sát xây dựng m t hệ th ng máy tính mới Bộ ộ ố ản đồ khu v c bao g m N thành ph và E ự ồ ố đường n i 2 chi u Các thành phố ề ố được đánh số
t 1 n ừ đế N
C nh sát mu n b t các t i ph m di chuy n t thành phả ố ắ ộ ạ ể ừ ố này đến thành ph ố khác Các điều tra viên, theo dõi bản đồ, phải xác định vị trí thiết lập trạm gác Hệ thống máy tính m i ph i trớ ả ả lời được 2 lo i truy v n ạ ấ sau:
Đầu tiên, Đối với hai thành ph A, B và mố ột đường nối gi a hai thành ph G1, ữ ố G2, h i t i ph m có th di chuy n t ỏ ộ ạ ể ể ừ A đến B nếu đường n i này b ố ị chặ (nghĩa là tên n
tội phạm không th s dể ử ụng con đường này) không?
Thứ hai, i v i 3 thành ph A, B, C, h i t i ph m có th di chuy n tđố ớ ố ỏ ộ ạ ể ể ừ A đến
B nếu như toàn bộ thành ph C b ki m soát ố ị ể (nghĩa là tên tội ph m không thạ ể đi vào thành phố này) không?
Dữ liệu đầu vào:
Dòng đầu tiên chứa 2 số nguyên N và E (2 ≤ N ≤ 100,000, 1 ≤ E ≤ 500,000), số thành phố và s ố đường nối
Trang 5M i dòng trong s E dòng ti p theo ch a 2 s nguyên phân bi t ỗ ố ế ứ ố ệ thuộc ph m vi [1, ạ N] - cho bi t nhãn c a hai thành ph n i v i nhau b i mế ủ ố ố ớ ở ột con đường Gi a hai thành ữ
ph ố có nhiều nh t mấ ột đường n ối
Dòng ti p theo ch a s nguyên Q ế ứ ố (1 ≤ Q ≤ 300,000), số truy v n ấ được th nghiử ệm trên hệ thống
M i dòng trong Q dòng ti p theo ch a 4 ho c 5 s nguyên Sỗ ế ứ ặ ố ố đầu tiên cho biết loại truy v : ấn 1 ho c 2 ặ
Nếu lo i truy v n là 1, ti p theo trên cùng dòng là 4 s nguyên G1, ạ ấ ế ố A, B, G2 ớ v i ý nghĩa như đã mô tả A khác G1, B; G2 mô tả một con đường có sẵn
Nếu lo i truy v n là 2, ti p theo trên cùng dòng là 3 s nguyên C vạ ấ ế ố A, B, ới ý nghĩa như đã mô tả C A, B, đôi một khác nhau
Dữ liệu được cho sao cho ban đầu luôn có cách di chuyển giữa hai thành phố bất kỳ
Kết quả:
Gồm Q dòng, m i dòng ch a câu tr l i cho m t truy v n N u câu tr l i là ỗ ứ ả ờ ộ ấ ế ả ờ
khẳng định, in ra ‘yes’ N u câu tr l i là phế ả ờ ủ định, in ra ‘no’
Bài toán trên là bi n th c a dế ể ủ ạng bài toán tìm đường đi trong đồ thị ớ v i các ràng buộc động Để ả gi i quy t bài toán này có nhiế ều phương pháp khác nhau với độ
ph c tứ ạp cũng khác nhau Trong báo cáo này, mục tiêu chính là tìm một phương pháp
x ử lý bài toán này một cách tối ưu nhất có th ể
Nội dung của báo cáo được chia làm b n ph n chính Bao g m: Phố ầ ồ ần 1 Cơ sở
lý thuy t là n i dung các ki n th c c n s dế ộ ế ứ ầ ử ụng để gi i quy t bài toán này, Ph n 2 ả ế ầ Phương pháp giải bài toán với nội dung các phương pháp giải và trình bày cách giải cho bài toán này, Ph n 3 Ví d minh h a v i nầ ụ ọ ớ ội dung là Đưa ra ví dụ cho phương pháp chúng tôi gi i b ng hình ả ằ ảnh minh h a và Ph n 4 tri n khai mã ngu n v i C/C++ ọ ầ ể ồ ớ
và gi i thích, ki m th ả ể ử mã nguồn
Trang 6CHƯƠNG 2 Nội dung
2.1 Cơ sở lý thuy t ế
Để giải quyết bài toán này, trước hết cần phải hiểu những lý thuyết quan tr ng ọ
s ẽ được sử ụng trong bài toán, đó là lý thuyế ề đồ thị và các cách để d t v duyệt đồ thị
Lý thuyết đồ thị
Đồ th trong lập trình là m t cấu trúc dữ liệu biểu diễn m i quan hệ giữa các ị ộ ố đối tượng Đồ thị G = (V, E) được hiểu là V đỉnh và E c nh Có nhi u kiạ ề ểu đồ thị như
đồ th vô hướị ng hoặc có hướng, đồ thị có thể có tr ng s hoặc không Để biểu diễn ọ ố
đồ thị trong lập trình, thông thường, đồ thị sẽ được cài đặt dưới dạng m t ma trận kề ộ
hoặc danh sách kề
Lý thuyết duyệt đồ ị th
Có hai cách duyệt đồ thị chính trong gi i thuả ật, đó là duyệt theo chi u r ng(BFS ề ộ – Breadth – First Search) và duyệt theo chiều sâu (DFS Depth First Search) m i – – ỗ cách duy t có kiệ ểu cài đặt khác nhau, qua đó đem đến s khác bi t vự ệ ề ưu điểm lần nhược điểm c a từng phương pháp duyệt ủ
Đầu tiên, đối v i BFS, là m t thu t toán duyớ ộ ậ ệt qua các đỉnh của đồ thị, BFS bắt
đầu từ m t đ nh g c và khám phá t t c ộ ỉ ố ấ ả các đỉnh bên cạnh ở mức hiện tại trước khi di chuyển đến các đỉnh ở m c ti p theo Th t duy t cứ ế ứ ự ệ ủa phương pháp này là duyệt theo
l p t gớ ừ ần đến xa nh t cấ ủa đỉnh gốc Phương pháp duyệt này mang lại nhiều ưu điểm trong các bài toán tìm đường đi ngắn nh t, các bài toán tìm ki m trên không gian r ng ấ ế ộ Tuy nhiên, thu t toán này trong m t s ậ ộ ố trường hợp các đồ thị l n, nó l i tiêu t n nhiớ ạ ố ều
b nh ộ ớ hơn so với DFS
Thứ hai, là thu t toán duyậ ệt đồ thị theo chi u sâu DFS, là m t thu t toán duyề ộ ậ ệt
ho c tìm ki m trên c u trúc cây hoặ ế ấ ặc đồ thị DFS hoạt động b ng cách khám phá càng ằ sâu càng tốt trước khi quay lui Cơ chế ủ c a DFS là s dử ụng ngăn xếp hoặc đệ quy để theo dõi các đỉnh cần khám phá theo thứ tự đi sâu nhấ ồi sau đó quay lui và khám t r phá các nhánh khác Ưu điểm của DFS đó là việc nó tiêu tốn ít bộ nhớ hơn so với BFS, cũng như dễ dàng cài đặt Còn nhược điểm của phương pháp này đó là có thể rơi vào vòng lặp vô hạn đối với đồ thị có chu trình Ứng dụng của phương pháp này vào gi i thuả ật đó là tìm kiếm các thành ph n liên thông, phát hiầ ện chu trình trong đồ thị, giải m t sộ ố bài toán như mê cung, sudoku
2.2 Phương pháp giải bài toán
Phân tích bài toán
Như đã trình bày bài toán ở trên, ta có thể thu gọn bài toán lại như sau: Cho một đồ thị G = (V, E) V tương đương với N thành ph , E là cố ạnh Là đồ thị các đường
Trang 7n i hai chi u (có th hiố ề ể ểu là đồ thị vô hướng) Ban đầu, sẽ luôn có cách di chuy n giể ữa hai đỉnh bất kì của đồ thị Cần trả lời hai câu truy vấn sau:
Câu truy v n m t: nấ ộ ếu xóa đường đi giữa hai đỉnh thì liệu hai đỉnh trong d ữ liệu vào ở trên đồ thị có còn liên thông không ?
Câu truy v n hai: n u bấ ế ỏ đi một đỉnh (b mỏ ột đỉnh và xóa toàn bộ đường kết
nối với đỉnh đó) thì đường đi giữa hai đỉnh được gửi vào có còn liên thông không ? Theo yêu cầu đề bài v i dớ ữ liệu đầu vào, ta thấy đồ thị trong bài nãy sẽ được
bi u di n theo d ng danh sách c nh ể ễ ạ ạ
Phương pháp giải bài toán
Từ bài toán đã cho, chúng tôi đã tìm hiểu, phân tích và suy ra được hai phương pháp giải bài này Phương pháp thứ nhất là phương án dễ cài đặt, còn phương án thứ hai mang tính tối ưu cao hơn
2.2.2.1 Phương án thứ nhất
Đây là một cách đơn giản để giải quyết bài toán này, đó là sử ụng phương d
thức duy t sâu DFS Cệ ụ ể th là đối với mỗi truy vấn, chúng ta sẽ ại blo ỏ đường/đỉnh tùy theo lo i truy vạ ấn, sau đó chúng ta sẽ ử ụng DFS để s d duyệt đồ thị ừ đỉ t nh xuất phát N u vế ẫn liên thông, nghĩa là đường đi giữa hai đỉnh này còn cách khác và in ra
‘yes’ ngược lại thì in ra ‘no’
Xét về mặt ưu điểm của cách này, đó là nó dễ cài đặt và tư duy không cần quá
ph c tứ ạp ở trong mã nguồn Cách này cũng rất kh thi nả ếu sử ụ d ng v i m t sớ ộ ố đồ thị
nh , không quá nhiỏ ều đỉnh Tuy nhiên, nhược điểm của phương pháp này là khi xử lý với các đồ thị l n, có th ớ ể lên đến hàng nghìn điểm và r t nhi u cấ ề ạnh,chưa kể v i nhiớ ều câu h i truy vỏ ấn thì phương pháp này sẽ chậm hơn và tốn bộ nhớ hơn so với phương pháp chúng tôi s trình bày ẽ ở ph n tiầ ếp theo Độ ph c t p cứ ạ ủa phương pháp này ở mức O(N*Q) với N là s thành ph và Q là s câu hố ố ố ỏi truy vấn
2.2.2.2 Phương án thứ hai
Ở phương án này, chúng tôi sử dụng thêm giải thuật Tarjan kết hợp với duyệt
đồ ị th sâu DFS Phương án này khả thi hơn so với phương án một khi độ ph c t p cứ ạ ủa
nó là O(Q log N) v i Q là s ớ ố câu hỏi truy v n và N là sấ ố đỉnh của đồ thị
Về gi i thu t Tarjan: ả ậ Giải thuật được đặt theo tên c a Robert Tarjan, m t nhà ủ ộ khoa học máy tính người M Gi i thu t này mỹ ả ậ ục đích để tìm ki m các thành ph n ế ầ liên thông mạnh trong đồ thị có hướng Đây là m gi i thu hi u qu vột ả ật ệ ả ới độ phức
tạp O(V + E), trong đó V là số đỉnh và E là s c nh cố ạ ủa đồ thị Thuật toán này được ứng dụng vào một số bài toán như: Tìm các thành phần liên thông mạnh trong đồ thị
có hướng, phát hiện chu trình trong đồ thị có hướng, tìm các c u và khầ ớp trong đồ thị
vô hướng Các bước của giải thuật này thường là:
Trang 8Đầu tiên, thực hiện duyệt đ th theo chiều sâu (DFS) ồ ị
Thứ hai, gán một số thứ ự t khám phá và m t giá tr low link cho mộ ị ỗi đỉnh Thứ ba, s d ng mử ụ ột ngăn xếp để lưu trữ các đỉnh đang được xét
Thứ tư, khi tìm thấy m t thành ph n liên thông m nh, lộ ầ ạ ấy các đỉnh ra khỏi ngăn
xếp cho đến khi gặp đỉnh gốc của liên thông mạnh đó
Về cách th c hi ự ện: Trong bài toán Cảnh sát, đây là một bài toán có d ng v ạ ề
vi c tìm c u và khệ ầ ớp trong đồ thị vô hướng Giải thuật Tarjan hoàn toàn làm được hai
d ng truy vạ ấn như yêu cầu V i d ng truy v n 1, là d ng bài toán tìm cớ ạ ấ ạ ầu trong đồ thị (M t cộ ạnh được gọi là c u n u viầ ế ệc xóa nó làm tăng số thành ph n liên thông cầ ủa đồ
thị.), còn v i dạng truy vấn 2, là dạng bài tìm khớp trong đồ ịớ th (Một đỉnh được gọi
là kh p n u vi c xóa nó và cùng v i các c nh kớ ế ệ ớ ạ ề làm tăng số thành ph n liên thông ầ
của đ thị) ồ
Trong bài này chúng tôi ứng d ng giụ ải thuật này như sau:
Đầu tiên, DFS từ 1 đỉnh trên đồ thị, lưu lại thứ tự duyệt đỉnh num[u] và đỉnh
thấp nhất trên đường đi mà đỉnh u n quay v đế ề được low[u] (low link)
Đố i với truy v n 1: D ng A, B, G1, G2: ấ ạ
C n ki m tra tính liên thông t A n B ầ ể ừ đế trên đồ thị không ch a c nh ứ ạ (G1,G2) Giả sử trên đường đi duyệt DFS ở trên, G1 duyệt trướ G2c (num[G1] < num[G2]), có
2 trường hợp xảy ra:
Trường hợp 1, G1 không ph i là cha tr c ti p c a ả ự ế ủ G2 trong lúc duy t DFS: tệ ức
t n tồ ại đường đi từ G1 đế G2 không đi qua cạn nh (G1,G2), do đó nếu xóa c nh này ạ thì tính liên thông đồ ị th không thay đổi, vậy nên A liên thông v i B (yes) ớ
G1 là cha tr c ti p c a ự ế ủ G2 trong lúc duyệt DFS: trong trường h p này, n u ợ ế
t ừ G2 có đường đi ngược lên các node thấp hơn G1 thì tính liên thông đồ thị không đổi (low[G2] <= num[G1]) (yes)
Sau cùng c n ki m tra vầ ể ị trí A và B so v i ớ G2, đảm b o r ng c 2 cùng thuả ằ ả ộc
hoặc không thuộc cây g c ố G2
Nếu không rơi vào trường hợp này, trả về ‘no’
Đố i với truy v n 2: D ng A, B, C ấ ạ
Giả s A ử được duyệt trước B trong quá trình DFS (num[A] < num[B]) Xét trường h p ợ A,B là 1 cạnh trực tiếp của đồ thị là cha tr(A ực tiếp c a ủ B) Xét trường h p B ợ được duyệt trong cây gốc A và ngược lại
Trang 9Ở từng trường hợp, cần kiểm tra vị trí của C với A, B, kiểm tra cha chung và low link giữa các node
Ưu điểm và nhược điểm:
Về ưu điểm của thuật toán này, rõ ràng nhất là thuật toán đượ ối ưu rấc t t tốt,
có độ phức tạp nhỏ hơn nhiều so với phương pháp ban đầu, đố ới các đồi v thị có số
đỉnh nh thì có thể mức chênh lệch không nhiều, tuy nhiên, khi s lượng đỉnh lên t i ỏ ố ớ hàng trăm, hàng nghìn thì sựchênh lệch về thời gian chạy đã rất khác biệt
Còn về nhược điểm của thuật toán này, dễ thấy là thu t toán này có s khó hi u ậ ự ể hơn và cũng khó cài đặt hơn
Ví dụ minh họa
Để có cái nhìn dễ ểu hơn về phương pháp này, có thể trình bày như sau: hi
Giả s ử có một input/output như sau:
INPUT OUTPUT
13 15
1 2
2 3
3 5
2 4
4 6
2 6
1 4
1 7
7 8
7 9
7 10
8 11
8 12
9 12
12 13
5
1 5 13 1 2
1 6 2 1 4
1 13 6 7 8
2 13 6 7
2 13 6 8
yes yes yes
no yes
T ừ input trên, có thể ẽ ra đồ thị như sau: v
Trang 10Input đầu vào gồm 5 truy vấn bao gồm 3 truy vấn 1 và 2 truy vấn loại hai Từng loại truy vấn có thể tính như sau: với truy vấn 1 nếu bỏ đi cầu 1 – 2 thì vẫn có đường khác
đi từ 5 – 13 nên ouput là yes Với truy vấn 2: Nếu bỏ đi cầu 1 – 4 thì vẫn có đường khác đi từ 6 – 2 nên output là yes Với truy vấn 3: Nếu bỏ đi cầu 7 – 8 thì vẫn có đường đi khác từ 13 – 6 nên output là yes Truy vấn 4: Nếu bỏ đi khớp 7 thì đồ thị từ
13 6 không còn liên thông nên ouput là no– Truy vấn 5: Nếu bỏ đi khớp 8 thì đồ thị
từ 13 – 6 vẫn liên thông nên output là yes
2.3 Xây dựng mã ngu n ồ
Sau th i gian nghiên cờ ứu, có đoạn mã ngu n x ồ ử lý bài toán trên như sau:
#include <bits/stdc++.h>
using namespace std;
#define pb push_back
#define FOR(i,l,r) for (int i=l;i<=r;i++)
Trang 11int n, m, low[N], num[N], pa[N][21];
int tin[N], tout[N], Time;
vector<int> a[N];
void dfs(int p, int u) {
tin[u] = ++Time;
num[u] = ++num[0];
low[u] = n+1;
pa[u][0] = p;
FOR(i,1,20) pa[u][i] = pa[pa[u][i-1]][i-1];
for (auto v : a[u]) {
if (v == p) continue;
if (num[v])
low[u] = min(low[u], num[v]);
else {
dfs(u,v);
low[u] = min(low[u], low[v]);
}
}
tout[u] = ++Time;
}
bool thuoc(int v, int u) {
return tin[u] <= tin[v] && tout[v] <= tout[u];
}
int FindParent(int p, int v) {
FOD(i,20,0) {
Trang 12}
return v;
}
bool solve1(int,int,int,int);
bool solve2(int,int,int);
int main() {
scanf("%d%d", &n,&m);
while (m ) {
int i, j;
scanf("%d%d", &i,&j);
a[i].pb(j);
a[j].pb(i);
}
dfs(0,1);
scanf("%d", &m);
while (m ) {
int t;
scanf("%d", &t);
if (t == 1) {
int A, B, G1, G2;
scanf("%d%d%d%d", &A, &B, &G1, &G2);
if (solve1(A, B, G1, G2)) puts("yes");
else puts("no");
} else {
int A, B, C;