1. Trang chủ
  2. » Giáo Dục - Đào Tạo

báo cáo các thuật toán rời rạc đã học ở chương 6 8

44 0 0
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Các Thuật Toán Rời Rạc Đã Học Ở Chương 6-8
Tác giả Trần Đức Long
Người hướng dẫn Trần Thế Vinh
Trường học Trường Đại Học Giao Thông Vận Tải TPHCM
Chuyên ngành Công Nghệ Thông Tin
Thể loại Báo cáo
Năm xuất bản 2024
Thành phố TPHCM
Định dạng
Số trang 44
Dung lượng 1,32 MB

Nội dung

CODE HOÀN THIỆNconstint MAX = 1000;vector adj[MAX];bool visited[MAX];//Thêm một cạnh vào đồ thị.void addEdgeintu, intv {//Xóa một cạnh khỏi đồ thị.void rmvEdgeintu, intv {adj[u].eraserem

Trang 1

BỘ GIÁO DỤC VÀ ĐẠO TẠO TRƯỜNG ĐẠI HỌC GIAO THÔNG VẬN TẢI TPHCM

KHOA CÔNG NGHỆ THÔNG TIN

BÁO CÁO

CÁC THUẬT TOÁN RỜI RẠC ĐÃ HỌC Ở CHƯƠNG 6-8

Giáo viên hướng dẫn: Trần Thế Vinh Sinh viên thực hiện: Trần Đức Long MSSV: 034205013204

LỚP: CN2301B

Trang 2

MỤC LỤC

I EULER

1.Khái niệm

2.Giải code

3.Giải bảng và sơ đồ

II HALMINTON

1.Khái niệm

2.Giải code

3.Giải bảng và sơ đồ

III PRIM

1.Khái niệm

2.Giải code

3.Giải bảng và sơ đồ

IV KRUSKAL

1.Khái niệm

2.Giải code

3.Giải bảng và sơ đồ V DJIKSTRA

1.Khái niệm

2.Giải code

3.Giải bảng và sơ đồ

VI FORD-BELLMAN

1.Khái niệm

2.Giải code

3.Giải bảng và sơ đồ

Trang 3

ĐÔI LỜI ĐẾN THẦY

Kính gửi thầy,

Nhân dịp này, con xin gửi đến thầy những lời tri ân chân thành về

sự hướng dẫn và giảng dạy của thầy trong suốt thời gian qua Thật lòng con rất biết ơn vì những kiến thức quý báu mà thầy đã truyền đạt và những hướng dẫn chân thành mà thầy luôn dành cho con.

Thời gian qua, nhờ sự chỉ bảo tận tình của thầy, con đã có cơ hội học hỏi và phát triển không chỉ về mặt kiến thức mà còn về mặt

kỹ năng và tư duy Những bài học thực tế và những phản hồi chi tiết từ thầy đã giúp con tự tin hơn trong việc nghiên cứu và viết tiểu luận.

Con sẽ luôn ghi nhớ những lời khuyên và chỉ dẫn của thầy, và cố gắng áp dụng vào từng bước tiến của mình Xin chân thành cảm

ơn thầy vì tất cả những điều tuyệt vời đã dành cho con trong suốt thời gian qua.

Kính chúc thầy luôn mạnh khỏe và thành công trong công việc giảng dạy, tiếp tục truyền đạt niềm đam mê và kiến thức cho thế

hệ học trò sau này.

Trân trọng,

Trần Đức Long

Trang 4

EULER – KHÁI NIỆM

- Đồ thị Euler là đồ thị có chu trình Euler.

- Đồ thị nửa Euler là đồ thị có đường đi Euler.

Thuật toán tìm chu trình Euler

-Đầu vào: Đồ thị Euler G = (V, E) biểu diễn bởi mảng các danh sách kề DK

- Đầu ra: Chu trình vô hưóng Euler với danh sách các đỉnh nằm trong stack EC.

Trang 5

Thêm cạch vào đồ thị nếu thiếu cạnh chẵn

void addEdge(int u, int v) {

adj[u].push_back(v);

adj[v].push_back(u);

}

Xóa một cạnh khỏi đồ thị nếu dư cạnh lẻ

void rmvEdge(int u, int v) {

adj[u].erase(remove(adj[u].begin(), adj[u].end(), v), adj[u].end()); adj[v].erase(remove(adj[v].begin(), adj[v].end(), u), adj[v].end()); }

Trang 6

Check cạnh tiếp theo có hợp lệ hay không

bool isValidNextEdge(int u, int v) {

if (adj[u].size() == 1)

return true;

bool visited[MAX];

memset(visited, false, sizeof(visited));

int count1 = DFSCount(u, visited);

rmvEdge(u, v);

memset(visited, false, sizeof(visited));

int count2 = DFSCount(u, visited);

Trang 7

Tìm một số đỉnh bắt đầu có số lẻ ( trường hợp cấp bách)

Trang 8

CODE HOÀN THIỆN

const int MAX = 1000;

vector < int > adj[MAX];

void rmvEdge( int u , int v ) {

adj[ u ].erase(remove(adj[ u ].begin(), adj[ u ].end(), v ), adj[ u ].end()); adj[ v ].erase(remove(adj[ v ].begin(), adj[ v ].end(), u ), adj[ v ].end()); }

//Thực hiện duyệt DFS để đếm số đỉnh có thể truy cập được từ một đỉnh int DFSCount( int v , bool visited []) {

Trang 9

bool isValidNextEdge( int u , int v ) {

if (adj[ u ].size() == 1)

return true ;

bool visited[MAX];

memset(visited, false , sizeof (visited));

int count1 = DFSCount( u , visited);

rmvEdge( u , v );

memset(visited, false , sizeof (visited));

int count2 = DFSCount( u , visited);

addEdge( u , v );

return (count1 <= count2);

}

//In đường đi Euler từ một đỉnh cụ thể.

void printEulerUtil( int u ) {

for ( int v : adj[ u ]) {

if (isValidNextEdge( u , v)) {

cout << u << "-" << v << " " ; rmvEdge( u , v);

printEulerUtil(v);

} }

Trang 11

 Kiểm tra tính hợp lệ của cạnh tiếp theo trong đường đi Euler:

cạnh từ u đến v có phải là cạnh hợp lệ để tiếp tục trong đường

đi Euler hay không Cạnh là hợp lệ nếu:

o Nó là cạnh duy nhất từ đỉnh u, hoặc

o Loại bỏ cạnh đó không làm giảm số đỉnh có thể truy cập

từ u.

 In đường đi Euler:

Hàm printEulerUtil(int u) in đường đi Euler bắt đầu

từ đỉnh u.

Hàm printEulerTour(int V) tìm và bắt đầu in đường đi

Euler từ đỉnh có số bậc lẻ đầu tiên (nếu có), hoặc từ đỉnh bất kỳ nếu tất cả các đỉnh đều có số bậc chẵn.

Trang 12

GIẢI TAY THEO LÝ THUYẾT

Cho đồ thị liên thông như hình ảnh như ta đếm được đỉnh

Suy ra đường đi của euler là 1-0-2-3-4-3-0 như kết quả

code chạy từng đỉnh và đường

HAMILTON-KHÁI NIỆM

Trang 13

Định nghĩa

- Đường Hamilton là đường đi qua mỗi đỉnh của đồ thị đúng một lần.

- Chu trình Hamilton là chu trình đi qua mỗi đỉnh của đồ thị đúng một lần

- Đồ thị Hamilton là đồ thị có chu trình Hamilton.

- Đồ thị nửa Hamilton là đồ thị có đường đi Hamilton.

Định lý Dirac

Nếu  a  V, deg(a)  (n/2) thì đồ thị vô hướng G(V,E) có chu trình Hamilton.

Nhận xét

1 Đồ thị có đỉnh bậc ≤ 1 thì không có chu trình Hamilton Nếu đồ thị

có các đỉnh đều có bậc  2 và có một đỉnh bậc 2 thì mọi chu trình Hamilton (nếu có) phải đi qua 2 cạnh kề của đỉnh này 3 Nếu trong

đồ thị có một đỉnh kề với 3 đỉnh bậc 2 thì không có chu trình

Hamilton 2

Trang 14

HAMILTON-CODE Thư viện

void printPath(vector<int>& path) {

for (int i = 0; i < path.size(); ++i) {

Check đỉnh kề với path ko

if (find(path.begin(), path.end(), v) != path.end()) return false;

Trang 15

if (find(adj[path[pos - 1]].begin(), adj[path[pos - 1]].end(), v) == adj[path[pos - 1]].end())

Trang 16

int V; // Số lượng đỉnh của đồ thị

void addEdge(int u, int v) {

Trang 17

for (int i = 0; i < path.size(); ++i) {

bool isSafe(int v, vector<int>& path, int pos) {

// Kiểm tra xem đỉnh v có kề với đỉnh cuối cùng trong path không

if (find(path.begin(), path.end(), v) != path.end())

return false;

// Kiểm tra xem có cạnh giữa đỉnh cuối cùng của path và đỉnh v không

if (find(adj[path[pos - 1]].begin(), adj[path[pos - 1]].end(), v) == adj[path[pos

- 1]].end())

return false;

return true;

}

bool hamiltonianCycleUtil(vector<int>& path, int pos) {

// Nếu tất cả các đỉnh đã được thăm

if (pos == V) {

// Kiểm tra xem có cạnh từ đỉnh cuối cùng của path về đỉnh đầu tiên không

if (find(adj[path[pos - 1]].begin(), adj[path[pos - 1]].end(), path[0]) !=

adj[path[pos - 1]].end()) {

Trang 20

Kết quả

TÓM TẮT CODE

 Thư viện và khai báo:

 Đoạn mã bao gồm các thư viện cần thiết: iostream để sử dụng các hàm

nhập/xuất và vector, algorithm để làm việc với các cấu trúc dữ liệu.

 Định nghĩa các hằng số và biến toàn cục như số lượng đỉnh tối đa và danh sách

kề để lưu trữ các cạnh của đồ thị.

 Hàm addEdge:

 Hàm này thêm một cạnh giữa hai đỉnh u và v trong đồ thị Nó sử dụng danh sách

kề adj để lưu trữ các cạnh này.

 Hàm printPath:

 Hàm này in ra đường đi Hamilton nếu tìm thấy Nó duyệt qua danh sách path và

in từng đỉnh trong đường đi.

 Hàm isSafe:

 Hàm này kiểm tra xem đỉnh v có thể được thêm vào đường đi hiện tại path tại vị trí pos hay không.

 Nó kiểm tra hai điều kiện:

1 Đỉnh v không được xuất hiện trong path trước đó.

2 Đỉnh v phải kề với đỉnh cuối cùng trong path.

Trang 21

 Nếu chưa thăm hết các đỉnh, nó thử thêm từng đỉnh vào path và gọi đệ quy để tiếp tục tìm kiếm Nếu không tìm thấy đường đi, nó quay lui và thử đỉnh tiếp theo.

 Hàm hamiltonianCycle:

 Hàm này khởi tạo danh sách path với kích thước bằng số đỉnh V và giá trị ban đầu là -1.

 Đặt đỉnh đầu tiên trong path là 0 (đỉnh bắt đầu của đường đi Hamilton).

 Gọi hàm hamiltonianCycleUtil để tìm chu trình Hamilton Nếu không tìm thấy, in ra thông báo không tồn tại đường đi Hamilton.

 Hàm main:

 Hàm main khởi tạo số đỉnh của đồ thị và thêm các cạnh vào đồ thị bằng cách gọi hàm addEdge.

 Cuối cùng, gọi hàm hamiltonianCycle để tìm và in chu trình Hamilton nếu có.

GIẢI CODE -TAY-SƠ ĐỒ TƯ DUY Giai theo lý thuyết

 Khởi tạo đồ thị:

 Đồ thị với các cạnh đã cho.

 Khởi tạo danh sách:

 Danh sách đánh dấu các đỉnh đã thăm.

 Danh sách lưu chu trình Hamilton.

Trang 23

o Thêm cạnh này vào cây khung nhỏ nhất.

o Thêm đỉnh mới vào tập hợp các đỉnh đã được thêm vào cây khung.

o Cập nhật danh sách các cạnh có thể được thêm vào cây khung.

Đầu vào: Đồ thị liên thông có trọng số.

Đầu ra: Cây khung nhỏ nhất của đồ thị.

Độ phức tạp: O(E log V) với E là số cạnh và V là số đỉnh khi sử

Trang 24

bool operator<( const Edge & other ) const {

vector < int > key(V, INT_MAX ); // Đặt tất cả các key là vô cùng

vector < bool > inMST(V, false ); // Đánh dấu các đỉnh đã được thêm vào MST

vector < int > parent(V, -1); // Để lưu cây khung nhỏ nhất

pq.push(make_pair(0, src)); // Thêm đỉnh bắt đầu vào hàng đợi ưu tiên

key[src] = 0; // Key của đỉnh bắt đầu là 0

while (!pq.empty()) {

int u = pq.top().second; // Lấy đỉnh có key nhỏ nhất

pq.pop();

if (inMST[u]) continue ;

inMST[u] = true ; // Đánh dấu đỉnh này đã được thêm vào MST

// Duyệt qua các đỉnh kề của u

for ( const auto & edge : adj[u]) {

int v = edge.first;

int weight = edge.second;

CODE HOÀN THIỆN

Trang 25

typedef pair < int , int > Edge ;

// Định nghĩa một đồ thị sử dụng danh sách kề (adjacency list)

vector < int > key(V, INT_MAX ); // Đặt tất cả các key là vô cùng

vector < bool > inMST(V, false ); // Đánh dấu các đỉnh đã được thêm vào MST

vector < int > parent(V, -1); // Để lưu cây khung nhỏ nhất

pq.push(make_pair(0, src)); // Thêm đỉnh bắt đầu vào hàng đợi ưu tiên

key[src] = 0; // Key của đỉnh bắt đầu là 0

while (!pq.empty()) {

int u = pq.top().second; // Lấy đỉnh có key nhỏ nhất

pq.pop();

if (inMST[u]) continue ;

inMST[u] = true ; // Đánh dấu đỉnh này đã được thêm vào MST

// Duyệt qua các đỉnh kề của u

for ( const auto & edge : adj[u]) {

int v = edge.first;

int weight = edge.second;

// Nếu đỉnh v chưa nằm trong MST và trọng số của cạnh u-v nhỏ hơn key[v]

if (!inMST[v] && key[v] > weight) {

Trang 26

cout << "nhap tung so" << endl;

for ( int i = 0; i < E; ++i) {

int u, v, w;

cin >> u >> v >> w;

graph.addEdge(u, v, w);

}

int result = graph.primMST();

cout << "Min (MST): " << result << endl; return 0;

}

KẾT QUẢ

Trang 27

Tóm Tắt

 Khởi tạo lớp Graph:

 Lớp này có hai thành phần chính là số đỉnh (V) và danh sách kề (adj) để lưu trữ các cạnh và trọng số.

 Có phương thức addEdge để thêm cạnh vào đồ thị.

 Thuật toán Prim (primMST):

 Sử dụng hàng đợi ưu tiên (priority_queue) để lựa chọn cạnh có trọng số nhỏ nhất để thêm vào cây khung.

 Bắt đầu từ một đỉnh ngẫu nhiên và cập nhật các trọng số (key) và đỉnh cha (parent) khi mở rộng cây khung.

 Duyệt qua các đỉnh kề của đỉnh hiện tại, cập nhật key và parent nếu tìm thấy cạnh có trọng số nhỏ hơn.

 Đầu vào và đầu ra:

 Đầu vào: Số đỉnh (V), số cạnh (E), và từng cạnh với trọng số tương ứng.

 Đầu ra: Trọng số của cây khung nhỏ nhất (result).

 Kết quả:

 Sau khi thực thi primMST, in ra trọng số của cây khung nhỏ nhất

PRIM-SƠ ĐỒ TƯ DUY

Trang 28

KRUSKAL-KHÁI NIỆM

 Đặc điểm chung:

 Kruskal là một thuật toán tham lam (greedy algorithm) có mục tiêu là chọn cạnh

có trọng số nhỏ nhất có thể để xây dựng cây khung nhỏ nhất.

 Cách hoạt động:

Bước 1: Sắp xếp tất cả các cạnh của đồ thị theo thứ tự tăng dần của trọng số.

Bước 2: Duyệt qua từng cạnh theo thứ tự đã sắp xếp Nếu cạnh này không tạo

thành chu trình khi được thêm vào cây khung hiện tại thì thêm nó vào cây khung đó.

Bước 3: Lặp lại cho đến khi cây khung nhỏ nhất được xây dựng hoàn chỉnh (bao

gồm V-1 cạnh với V là số đỉnh của đồ thị).

 Điều kiện để thực hiện Kruskal:

 Đồ thị phải là đồ thị vô hướng và có thể là đồ thị có trọng số.

 Đồ thị không có chu trình âm.

Trang 29

vector < Edge > edges; // Danh sách các cạnh

Graph( int V , int E ) {

this ->V = V ;

this ->E = E ;

}

Hàm hợp nhất hai tập

void unite( vector < int >& parent , vector < int >& rank , int x , int y ) {

int rootX = find( parent , x );

int rootY = find( parent , y );

if ( rank [rootX] < rank [rootY]) {

parent [rootX] = rootY;

}

else if ( rank [rootX] > rank [rootY]) {

parent [rootY] = rootX;

vector < int > parent(V);

vector < int > rank(V, 0);

Trang 30

parent[i] = i;

}

int result = 0; // Tổng trọng số của MST

int edgeCount = 0; // Số cạnh đã thêm vào MST

for ( const auto & edge : edges) {

if (edgeCount == V - 1) break ; // Nếu đã đủ (V-1) cạnh, thoát khỏi vòng lặp

int u = edge.u;

int v = edge.v;

int w = edge.w;

int setU = find(parent, u);

int setV = find(parent, v);

// Nếu u và v thuộc hai tập khác nhau, thêm cạnh này vào MST

vector < Edge > edges; // Danh sách các cạnh

Graph( int V , int E ) {

Trang 31

}

// Hàm hợp nhất hai tập

void unite( vector < int >& parent , vector < int >& rank , int x , int y ) {

int rootX = find( parent , x );

int rootY = find( parent , y );

if ( rank [rootX] < rank [rootY]) {

parent [rootX] = rootY;

}

else if ( rank [rootX] > rank [rootY]) {

parent [rootY] = rootX;

vector < int > parent(V);

vector < int > rank(V, 0);

// Khởi tạo các tập ban đầu

for ( int i = 0; i < V; ++i) {

parent[i] = i;

}

int result = 0; // Tổng trọng số của MST

int edgeCount = 0; // Số cạnh đã thêm vào MST

for ( const auto & edge : edges) {

if (edgeCount == V - 1) break ; // Nếu đã đủ (V-1) cạnh, thoát khỏi vòng lặp

int u = edge.u;

int v = edge.v;

int w = edge.w;

int setU = find(parent, u);

int setV = find(parent, v);

// Nếu u và v thuộc hai tập khác nhau, thêm cạnh này vào MST

Trang 32

cout << "dinh va canh : " ;

cin >> V >> E;

Graph graph(V, E);

cout << "Nhap so va cac canh :" << endl;

for ( int i = 0; i < E; ++i) {

int u, v, w;

cin >> u >> v >> w;

graph.addEdge(u, v, w);

}

int result = graph.kruskalMST();

cout << "Min (MST): " << result << endl;

Trang 33

0 - 3 với trọng số 5

0 - 1 với trọng số 10 Tổng lại bằng 19.

DIJSKTRA-KHÁI NIỆM

Các đặc điểm chính của thuật toán Dijkstra:

Loại đồ thị: Đồ thị có hướng hoặc vô hướng, có thể có trọng số

dương.

Điểm xuất phát: Một đỉnh bất kỳ trong đồ thị.

Bảng tính toán: Sử dụng bảng để lưu trữ khoảng cách ngắn

nhất từ điểm xuất phát đến các đỉnh còn lại.

Cơ chế hoạt động: Dựa trên việc mở rộng đường đi ngắn nhất

từ điểm xuất phát, sau đó cập nhật và chọn đỉnh tiếp theo để

mở rộng dựa trên khoảng cách hiện tại.

Các bước cơ bản của thuật toán:

1 Khởi tạo: Đặt khoảng cách từ điểm xuất phát đến chính nó là 0

và đặt khoảng cách từ điểm xuất phát đến các đỉnh khác là vô cùng.

2 Chọn đỉnh: Chọn đỉnh có khoảng cách ngắn nhất chưa được

Ngày đăng: 11/07/2024, 15:52

TÀI LIỆU CÙNG NGƯỜI DÙNG

  • Đang cập nhật ...

TÀI LIỆU LIÊN QUAN

w