HỌC VIỆN CÔNG NGHỆ BƯU CHÍNH VIỄN THÔNG BÀI TẬP LỚN MÔN HỌC TOÁN RỜI RẠC NHÓM MÔN HỌC 02 Giảng viên Dương Thị Thanh Tú Sinh viên Nguyễn Khôi Nguyên Nguyễn Đức Lân Nguyễn Trung Đức Nguyễn Bá Phúc Nhóm[.]
Trang 1HỌC VIỆN CÔNG NGHỆ BƯU CHÍNH VIỄN THÔNG
BÀI TẬP LỚN
MÔN HỌC: TOÁN RỜI RẠC NHÓM MÔN HỌC: 02
Giảng viên: Dương Thị Thanh Tú Sinh viên: Nguyễn Khôi Nguyên
Nhóm Bài tập lớn : 1_5
Hà Nội năm 2022
Trang 2Chương I Giới thiệu và mục đích của đề tài 4
1 Giới thiệu đề tài 4
a Chu trình Euler(đồ thị Euler): 4
b Chu trình Hamilton(đồ thị Hamilton): 4
Chương II Kiến thức cơ sở 5
1 Đồ thị Euler 5
2 Thuật toán tìm chu trình Euler 5
2.1 Chứng minh đồ thị là Euler 6
2.2 Biểu diễn thuật toán tìm chu trình Euler 6
3 Đồ thị Hamilton 7
4 Thuật toán tìm tất cả các chu trình Hamilton 7
4.1 Chứng minh chu trình là Hamilton 7
4.2 Biểu diễn thuật toán tìm chu trình Hamilton 7
Chương III Ứng dụng 8
A Bài toán Người đưa thư 8
I - Phát biểu về bài toán người đưa thư 8
1 Phát biểu bài toán: 8
2 Cách giải: 8
II – Phương pháp giải và kiểm nghiệm 9
1 Phương pháp giải: 9
2 Kiểm nghiệm: 9
III –Chương trình 10
1 Bài toán người đưa thư 10
2 Cài đặt thuật toán 11
B Bài toán Người đi du lịch 16
I - Phát biểu về bài toán người đi du lịch 16
1 Phát biểu bài toán: 16
2 Cách giải: 16
II – Chương trình 16
1 Bài toán người đi du lịch 16
2.Cài đặt thuật toán 17
Trang 4Chương I Giới thiệu và mục đích của đề tài
1 Giới thiệu đề tài
a Chu trình Euler(đồ thị Euler):
Là khái niệm xuất phát từ bài toán 7 cây cầu do Euler giải quyết vào khoảng năm
1737 Người dân trong thành phố đặt ra câu hỏi là thử tìm cách xuất phát từ một vùng đi,
đi qua tất cả 7 chiếc cầu, mỗi chiếc đi qua đúng 1 lần và trở về nơi xuất phát
đỉnh ứng với một vùng, mỗi cạnh ứng với một chiếc cầu
chu trình có dạng như mong muốn chỉ tồn tại khi và chỉ khi không có nút bậc lẻ Một đường
đi như vậy được gọi là một chu trình Euler
b Chu trình Hamilton(đồ thị Hamilton):
Khái niệm về đường đi và chu trình Hamilton được đưa ra bởi William Rowan Hamilton vào năm 1856, sau khi ô thiết kế một trò chơi trên khối đa diện 20 đỉnh, 30 cạnh,
12 mặt, mỗi mặt là một ngũ giác đều và người chơi cần chọn các cạnh để thành lập một đường đi qua 5 đỉnh cho trước(còn gọi là trò chơi Icosian)
Trang 5Bài toán tổng quát được Hamilton đưa ra là: Tìm một chu trình đơn đi qua tất cả các đỉnh trên đồ thị, mỗi đỉnh đúng một lần sau đó quay trở về điểm xuất phát Cho đến nay, bài toán này vẫn chưa có lời giải đối với trường hợp đồ thị tổng quát Những bài toán liên quan tới chu trình Hamilton đều là những bài toán NP-hard Mọi thuật toán tìm kiếm chu trình Hamilton hiện nay đều chỉ thiết kế dựa trên mô hình duyệt mà thôi
2 Mục đích:
Giúp mọi người có thêm kiến thức về lý thuyết đô thị và hiểu hơn về đồ thị Euler
và Hamilton
Giúp giải quyết các bài toán ứng dụng
Chương II Kiến thức cơ sở
1 Đồ thị Euler
Định nghĩa:
- Chu trình đơn trong đồ thị G đi qua mỗi cạnh của đồ thị đúng một lần được gọi là chu trình Euler
- Đồ thị được gọi là đồ thị Euler nếu nó có chu trình Euler
2 Thuật toán tìm chu trình Euler
Để tìm một chu trình Euler của đồ thị ta sử dụng kết quả của định lý sau
Định lý 1 Điều kiện cần và đủ để đồ thị G=<V,E> là Euler Đồ thị vô hướng liên
thông G=<V, E> là đồ thị Euler khi và chỉ khi mọi đỉnh của G đều có bậc chẵn Đồ thị có hướng liên thông yếu G=<V, E> là đồ thị Euler khi và chỉ khi tất cả các đỉnh của nó đều
có bán đỉnh bậc ra bằng bán đỉnh bậc vào (điều này làm cho đồ thị là liên thông mạnh)
Trang 62.1 Chứng minh đồ thị là Euler
Đối với đồ thị vô hướng, để chứng minh đồ thi có là Euler hay không ta chỉ cần
thực hiện:
• Kiểm tra đồ thị có liên thông hay không? Điều này dễ dàng thực hiện bằng
cách kiểm tra DFS(u) = V hoặc BFS(u) = V thì ta kết luận đồ thị là liên thông
(u là đỉnh bất kỳ của đồ thị)
• Sử dụng tính chất của ma trận kề biểu đồ thị vô hướng để tính toán bậc của các đỉnh Đối với đồ thị có hướng, để chứng minh đồ thi có là Euler hay không ta chỉ cần
thực hiện:
• Kiểm tra đồ thị có liên thông yếu hay không? Điều này dễ dàng thực hiện bằng
cách kiểm tra nếu tồn tại đỉnh u V để DFS(u) = V hoặc BFS(u) = V thì ta kết luận đồ thị
là liên thông yếu
• Sử dụng tính chất của ma trận kề biểu đồ thị có hướng để tính bán đỉnh bậc ra
và bán đỉnh bậc vào của các đỉnh Bán đỉnh bậc ra của đỉnh u là deg+(u) là số
các số 1 của hàng u Bán đỉnh bậc vào của đỉnh u là deg-(u) là số các số 1 của
cột u
2.2 Biểu diễn thuật toán tìm chu trình Euler
Thuật toán Euler-Cycle(u):
Bước 1 (Khởi tạo) :
stack =;
CE = ;
//Khởi tạo một stack bắt đầu là //Khởi tạo mảng CE bắt đầu là Push (stack, u) ; //Đưa đỉnh u vào ngăn xếp
Bước 2 (Lặp ):
while (stack ) do { //Lặp cho đến khi stack rỗng
s = get(stack); //Lấy đỉnh ở đầu ngăn xếp
if ( Ke(s) ) then { // Nếu danh sách Ke(s) chưa rỗng
t =< Đỉnh đầu tiên trong Ke(s)>;
Push(stack, t)‟ //Đưa t vào stack;
E = E \ (s,t); // Loại bỏ cạnh (s,t);
}
else { //Trường hợp Ke(s)=
s = Pop(stack);// Đưa s ra khỏi ngăn xếp
Trang 7s CE; //Đưa s sang CE
}
Bước 3 (Trả lại kết quả) :
<Lật ngược lại các đỉnh trong CE ta được chu trình Euler> ;
3 Đồ thị Hamilton
- Chu trình bắt đầu tại một đỉnh v nào đó đi qua tất cả các đỉnh còn lại mỗi đỉnh đúng một lần sau đó quay trở lại v được gọi là chu trình Hamilton
- Đồ thị có chu trình Hamilton được gọi là đồ thị Hamilton
4 Thuật toán tìm tất cả các chu trình Hamilton
Một số quy luật khi tìm chu trình Hamilton:
Cho đồ thị G=< V,E >
• 1.Nếu đỉnh v có bậc < 2: Không có chu trình Hamilton
• 2.Nếu đỉnh v có bậc = 2 thì cả 2 cạnh đều xuất hiện trong chu trình Hamilton
• 3.Khi đã chọn 2 cạnh nào đó của 1 đỉnh, xóa tất cả các cạnh còn lại
• 4.Nếu đỉnh v kề với > 2 đỉnh bậc 2 => Không có chu trình Hamilton
4.1 Chứng minh chu trình là Hamilton
Để tìm chu trình Hamilton của đồ thị ta có thể dựa vào các định lý sau
Định lý 1: Xét với đồ thị vô hướng G, trong đó tồn tại k đỉnh sao cho nếu xóa đi k
đỉnh này cùng với những cạnh liên thuộc của chúng thì đồ thị nhận được sẽ có nhiều hơn k thành phần liên thông Khi đó, khẳng định G không phải đồ thị Hamilton
Định lý 2: (Dirak,1952): Xét đơn đồ thị vô hướng G = (V, E) có n đỉnh (n ≥ 3) Nếu
mọi đỉnh đều có bậc không nhỏ hơn [n/2] thì G là đồ thị Hamilton
Định lý 3: (Ghouila – Houiri, 1960): Xét đơn đồ thị có hướng liên thông mạnh G =
(V, E) có n đỉnh Nếu trên phiên bản vô hướng của G, mọi đỉnh đều có bậc không nhỏ hơn n thì G là đồ thị Hamilton
Định lý 4: (Ore, 1960): Xét đơn đồ thị vô hướng G = (V, E) có n đỉnh (n ≥3) Xét
mọi cặp đỉnh không kề nhau, nếu không có cặp đỉnh nào tổng bậc nhỏ hơn n thì G
là đồ thị Hamilton
Định lý 5: (Meynie, 1973): Xét đơn đồ thị có hướng liên thông mạnh G = (V, E) có
n đỉnh Nếu trên phiên bản vô hướng của G, mọi cặp đỉnh không kề nhau đều có tổng bậc không nhỏ hơn 2n-1 thì G là đồ thị Hamilton
4.2 Biểu diễn thuật toán tìm chu trình Hamilton
Thuật toán liệt kê tất cả các chu trình Hamilton bắt đầu tại đỉnh thứ k
Trang 8Hamilton(int k){
for(y ∈ Ke(X[k-1])){
if((k == n+1) && (y == v0))
Ghinhan(X[1],X[2],…,X[n], v0);
else if(chuaxet[y] == true){
X[k] = y;
chuaxet[y] = false;
Hamilton(k+1);
chuaxet[y] = true;
}
}
}
Khi đó, việc liên kết các chu trình Hamilton được thực hiện như sau:
Hamilton-Cycle(v0){
//Khởi tạo các đỉnh là chưa xét for(v ∈ V)
chuaxet[v] = true;
X[1] = v0; //v0 là một đỉnh nào đó của đồ thị
chuaxet[v0] = false; //Đánh dấu v0 đã xét
Hamilton(2); //Gọi thủ tục duyệt
}
Chương III Ứng dụng
A Bài toán Người đưa thư
I - Phát biểu về bài toán người đưa thư
1 Phát biểu bài toán:
MỘT NGƯỜI ĐƯA THƯ XUẤT PHÁT TỪ BƯU ĐIỆN PHẢI ĐẾN MỘT SỐ CON ĐƯỜNG ĐỂ PHÁT THƯ RỒI QUAY TRỞ VỀ ĐIỂM XUẤT PHÁT, HỎI
NGƯỜI ĐÓ PHẢI ĐI NHƯ THẾ NÀO ĐỂ SỐ ĐƯỜNG ĐI LÀ NGẮN NHẤT ?
2 Cách giải:
Ta xét bài toán ở một dạng đơn giản như sau :
Cho đồ thị vô hướng, liên thông có trọng số G<V, E> Một chu trình qua mọi cạnh của G gọi là một hành trình trong G Trong các hành trình đó, hãy tìm hành trình ngắn nhất
Trang 9Rõ ràng nếu G là đồ thị Euler (mọi đỉnh đều có bậc chẵn) thì chu trình Euler trong
G (qua mỗi cạnh của G đúng một lần ) là hành trình ngắn nhất cần tìm
Chỉ còn phải xét trường hợp G có một số đỉnh bậc lẻ (số đỉnh bậc lẻ là một số chẵn) Khi đó, mọi hành trình trong G đi qua ít nhất 2 lần một số cạnh nào đó
3 Dữ liệu đầu vào
Biểu diễn đồ thị liên thông G bằng ma trận kề có trọng số Với các đỉnh không có cạnh nối thì giá trị là 0
II – Phương pháp giải và kiểm nghiệm
1 Phương pháp giải:
ĐỒ THỊ LIÊN THÔNG G VÔ HƯỚNG CÓ TRỌNG SỐ
Bước 1: Ktra trong đồ thị G có chu trình Euler hay ko? Nếu tồn tại thì chỉ cần tính tổng trọng số trong đồ thị(ta ko thể có một lộ trình ngắn hơn vì ta phải ghé thăm các đỉnh
ít nhất một lần) Ngược lại, nếu ko tồn tại chu trình Euler thì tiếp tục thực hiện Bước 2
Bước 2: Tìm tất cả đỉnh có bậc lẻ
Bước 3: Liệt kê tất cả các cặp đỉnh bậc lẻ có thể có (đối với n đỉnh bậc lẻ, tổng số các cặp có thể có là (n-1)*(n-3)*(n-5)…*1
Bước 4: Đối với mỗi cặp ghép nối, hãy tìm đường đi ngắn nhất nối chúng
Bước 5: Tìm các cặp mà đường đi nối chúng ngắn nhất là nhỏ nhất
Bước 6: Cập nhật các cạnh vừa tìm được ở bước 5 vào đồ thị G
Bước 7: Độ dài ngắn nhất chính là tổng trọng số trong đồ thị G mới
Bước 8: In ra mạch Euler của đồ thị đã sửa đổi
2 Kiểm nghiệm:
Trang 10Bước 1: Xét đồ thị trên, có deg(a)=deg(b)=deg(f)=deg(e)=3(lẻ)
=> ko có chu trình Euler
Bước 2: Các đỉnh có bậc lẻ : A, B, E, F
Bước 3: Các cặp đỉnh bậc lẻ có thể có: [AE, BF], [AB, EF], [AF, BE]
Bước 4: Đường đi ngắn nhất nối các cặp đỉnh:
AE = AC + CE = 2+1 = 3
BF = BD + DF = 1+1 = 2
AB = 3
EF = 4
AF = AB+BD+DF = 5
BE = BA+AC+CE = 6 Bước 5: Cặp đỉnh có trọng số nhỏ nhất mà đi qua đỉnh bậc lẻ tìm được là [ae, bf] Bước 6: Cập nhật đồ thị, ưu tiên đi những cạnh có nhiều lần lặp lại trước
Bước 7: Độ dài đường đi ngắn nhất là 28
Bước 8: Đường đi tìm được :
A - C - E - A - B - D - F - B - D - F - E - C – A
III – Chương trình
1 Bài toán người đưa thư
Trang 112 Cài đặt thuật toán
#include<bits/stdc++.h>
using namespace std;
#define MAX 50
struct DoubleVertex{
int weight;
vector<int> path;
};
int V, len = 0;
int a[MAX][MAX];
vector<int> odd_vertex;
vector<int> cycle;
map<pair<int, int>, DoubleVertex> shortest_path;
vector<pair<int, int> > Set;
bool used[MAX]; int x[10];
int res = 1e9;
int deg[MAX];
int c[MAX][MAX];
// ham nhap gia tri
void nhap(void){
cin >> V;
cout<<"So dinh cua do thi n = "<< V <<endl;
// nhap ma trong so
cout << "Ma tran trong so cua do thi: \n";
for(int i=1; i<=V;i++)
{
6
0 3 1 0 5 0
3 0 0 1 0 6
1 0 0 0 2 0
0 1 0 0 0 1
5 0 2 0 0 4
0 6 0 1 4 0
28
1 3 5 1 2 4 6 2 4 6 5 3 1
Trang 12for(int j=1; j<=V;j++)
{
cin >> a[i][j];
if(a[i][j] != 0)
c[i][j] = 1;
deg[i]++;
}
}
}
}
// ham phan hoach cac dinh bac le su dung quay lui hoan vi
void Try(int i){
if(i == odd_vertex.size()){
bool ok = true;
vector<pair<int, int> > tmp;
for(int k = 0; k < i; k += 2){
if(odd_vertex[x[k]] > odd_vertex[x[k + 1]]){
ok = false;
break;
} tmp.push_back({odd_vertex[x[k]], odd_vertex[x[k + 1]]}); }
if(ok){
for(int k = 0; k < tmp.size() - 1; k++){
if(tmp[k].first > tmp[k + 1].first){
ok = false;
break;
} }
if(ok){
int sum = 0;
for(int k = 0; k < tmp.size(); k++){
sum += shortest_path[tmp[k]].weight;
} if(sum < res){
res = sum;
Set = tmp;
}
Trang 13} }
}
for(int j = 0; j < odd_vertex.size(); j++){
if(used[j] == false){
x[i] = j;
used[j] = true;
Try(i + 1);
used[j] = false;
} }
}
// kiem tra chu trinh Euler
bool checkEulerCycle(){
for(int i = 1; i <= V; i++){
if(deg[i] % 2){ //neu bac cua dinh la le thi deg[i]%2==true
odd_vertex.push_back(i);
} }
if(odd_vertex.size() == 0) return true; // khong co bac le nao thi return true else return false;
}
// ham tim chu trinh Euler va trong so
void EulerCycle(){
stack<int> st;
st.push(1);
while(!st.empty()){
int u = st.top();
int priority = 0;
int cnt = 0;
for(int i = 1; i <= V; i++){
if(c[u][i] > cnt){
priority = i;
cnt = c[u][i];
} }
if(cnt != 0){
Trang 14len += a[u][priority];
st.push(priority);
c[u][priority] ; c[priority][u] ;
} else{
st.pop();
cycle.push_back(u);
} }
cout << "Do dai duong di ngan nhat la: " << len << endl;
reverse(cycle.begin(), cycle.end());
for(int i = 0; i < cycle.size(); i++){
cout << (char)(cycle[i]+'A'-1) << " ";
}
}
// ham tim duong di nho nhat tu dinh le den cac dinh con lai(thuat toan Dijkstra)
void shortestPath(int u){
int d[V + 1];
for(int i = 1; i <= V; i++){
d[i] = 1e9;
}
d[u] = 0;
int parent[V + 1];
parent[u] = u;
priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > q; q.push({0, u});
while(!q.empty()){
pair<int, int> top = q.top();
q.pop();
int v = top.second;
int w = top.first;
if(w > d[v]) continue;
for(int i = 1; i <= V; i++){
if(a[v][i] != 0){
if(a[v][i] + d[v] < d[i]){
d[i] = d[v] + a[v][i];
q.push({d[i], i});
parent[i] = v;
Trang 15} }
} }
for(int i = 1; i <= V; i++){
if(deg[i] % 2 == 1 && u < i){
vector<int> tmp;
int s = i;
while(s != u){
tmp.push_back(s);
s = parent[s];
} tmp.push_back(u);
reverse(tmp.begin(), tmp.end());
DoubleVertex x = {d[i], tmp};
shortest_path[{u, i}] = x;
} }
}
int main(){
memset(deg, 0, sizeof(deg));
memset(c, 0, sizeof(c));
nhap();
if(checkEulerCycle()){
EulerCycle();
}
else{
for(int i = 0; i < odd_vertex.size(); i++){
shortestPath(odd_vertex[i]);
} memset(used, false, sizeof(used));
Try(0);
for(int i = 0; i < Set.size(); i++){
vector<int> tmp = shortest_path[{Set[i].first, Set[i].second}].path;
for(int j = 0; j < tmp.size() - 1; j++){
c[tmp[j]][tmp[j + 1]]++;
c[tmp[j + 1]][tmp[j]]++;
}
Trang 16} cout << endl;
EulerCycle();
}
}
B Bài toán Người đi du lịch
I - Phát biểu về bài toán người đi du lịch
1 Phát biểu bài toán:
MỘT NGƯỜI XUẤT PHÁT TỪ MỘT THÀNH PHỐ NÀO ĐÓ MUỐN TỚI THĂM N−1 THÀNH PHỐ KHÁC, MỖI THÀNH PHỐ ĐÚNG MỘT LẦN, RỒI QUAY
VỀ THÀNH PHỐ BAN ĐẦU HỎI NÊN ĐI THEO TRÌNH TỰ NÀO ĐỂ ĐỘ DÀI TỔNG CỘNG CÁC ĐOẠN ĐƯỜNG ĐI QUA LÀ NGẮN NHẤT
Bài toán Người du lịch chỉ là mô hình tiêu biểu của ứng dụng chu trình Hamilton Bài toán còn được hiểu là tìm chu trình Hamilton với độ dài đường đi nhỏ nhất
2 Cách giải:
Gọi C = { cij : i,j = 1,2, ,n} là ma trận chi phí
Mỗi hành trình: v = v(1)→v(2)→ →v(n-1)→v(n)→v(1) có thể viết dưới dạng:
v = (v(1),v(2)), (v(2),v(3)), , (v(n-1),v(n)), (v(n),v(1)) Trong đó, mỗi thành phần (v(i-1),v(i)) gọi là một cạnh của hành trình
Trong bài toán người du lịch khi tiến hành tìm kiếm lời giải ta sẽ phân tập hành trình thành hai tập con: Một tập chứa cạnh (i,j) và tập không chứa cạnh này Ta gọi việc
đó là phân nhánh, mỗi tập con nói trên gọi là nhánh
3 Dữ liệu đầu vào
Biểu diễn đồ thị liên thông G bằng ma trận kề có trọng số Với các đỉnh không có cạnh nối thì giá trị là 0
II – Chương trình
1 Bài toán người đi du lịch
5 7
1 2 3
2 3 7
5 3 5
4 3 6
5 4 2
So dinh: 5 So canh: 7
CHU TRINH HAMILTON LA: A B E C D A CHI PHI NHO NHAT LA: 20