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

giáo trình thực hành lý thuyết đồ thị

23 714 0

Đ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

Định dạng
Số trang 23
Dung lượng 420,67 KB

Nội dung

đỉnh trong cùng một miền liên thông có đường đi trực tiếp/gián tiếp đến nhau; giữa các đỉnh khác miền liên thông không tồn tại đường đi này.. THU ẬT TOÁN TÌM SỐ THÀNH PHẦN LIÊN THÔNG Bư

Trang 2

M ỤC LỤC

Chương 1: Biểu diễn đồ thị trên máy tính 3

Chương 2: Liên thông và các thành phần liên thông 8

Chương 3: Cây và cây bao trùm 12

Chương 4: Thuật toán Prim 14

Chương 5: Cây bao trùm nhỏ nhất & Thuật toán Kruskal 16

Chương 6: Tìm đường trong đồ thị 18

Chương 7: Tìm đường trong đồ thị - Thuật toán Dijkstra 20

Chương 8: Tìm đường trong đồ thị - Thuật toán Floyd 22

Trang 3

Chương 1: Biểu diễn đồ thị trên máy tính

I NH ẮC LẠI LÝ THUYẾT

1 MA TR ẬN KỀ (ADJACENCY MATRIX)

- Chi phí duyệt các cạnh kề của đỉnh i luôn luôn cố định là

Trang 4

2 DANH SÁCH C ẠNH (EDGE LIST)

Trong trường hợp đồ thị có n đỉnh, m cạnh, ta có thể biểu diễn đồ thị dưới dạng danh

3 DANH SÁCH K Ề (ADJACENCY LIST)

Để khắc phục nhược điểm của phương pháp biểu diễn đồ thị bằng ma trận kề và danh

chứa các đỉnh kề với nó

Ví d ụ

Trang 5

Danh sách kề tương ứng

Hoặc:

Các tính ch ất của danh sách kề

Ưu điểm:

- Tiết kiệm chi phí lưu trữ

Trang 6

T ổ chức dữ liệu

Để biểu diễn ma trận, ta dùng một mảng hai chiều kiểu nguyên

#define MAX 100 struct GRAPH { int n;

int a[MAX][MAX];

};

Đọc ma trận kề từ tập tin vào mảng

void ReadGraph(GRAPH &g, char *fn) {

// m ở file, nếu không mở được file sẽ báo lỗi và thoát FILE * f = fopen(fn, “rt”);

if (f == NULL) {

exit(0);

} // đọc giá trị đỉnh của đồ thị vào biến n fscanf(f, “%d”, &g.n);

// đọc giá trị của ma trận a từ file int i, j;

for (i=0; i<g.n; i++)

for (j=0; j<g.n; j++)

fscanf(f, “%d”, &g.a[i][j]);

// đóng file nhập fclose(f);

}

Xu ất ma trận kề ra màn hình

void PrintGraph(GRAPH &g) {

// in ra s ố đỉnh của đồ thị printf(“%d\n”, g.n);

for (int i=0; i<g.n; i++) { for (int j=0; j<g.n; j++)

printf(“%d\t”, g.a[i][j]);

printf(“\n”);

} }

2 CÀI ĐẶT DANH SÁCH CẠNH VÀ DANH SÁCH KỀ

Xem như bài tập

Trang 7

III BÀI T ẬP

định dạng ở phần 2 In ra màn hình các giá trị sau đây:

G có hướng hay vô hướng

có tồn tại chu trình Euler hay không? Nếu có, hãy chỉ ra một chu trình Euler của đồ thị này

định dạng ở phần 2 In ra tập tin GRAPH.EL theo định dạnh danh sách cạnh

theo định dạng ở phần 2 In ra tập tin GRAPH.AM theo định dạnh ma trận kề

định dạng ở phần 2 In ra tập tin GRAPH.AL theo định dạnh danh sách kề

định dạng ở phần 2 In ra tập tin GRAPH.AM theo định dạnh ma trận kề

họa

Trang 8

Chương 2: Liên thông và các thành phần liên thông

I NH ẮC LẠI LÝ THUYẾT

1 ĐỒ THỊ LIÊN THÔNG

Đối với đồ thị có hướng, ta có hai khái niệm liên thông mạnh và liên thông yếu Việc

kiểm tra một đồ thị có hướng có liên thông yếu hay không về độ phức tạp tương đương với việc kiểm tra một đồ thị vô hướng có liên thông hay không Bài hướng dẫn

hay không

đỉnh trong cùng một miền liên thông có đường đi trực tiếp/gián tiếp đến nhau; giữa các đỉnh khác miền liên thông không tồn tại đường đi này) Như vậy, một đồ thị liên thông là một đồ thị chỉ có duy nhất một miền liên thông

2 THU ẬT TOÁN TÌM SỐ THÀNH PHẦN LIÊN THÔNG

Bước 1: Đánh dấu tất cả các đỉnh trong đồ thị là chưa duyệt Biến đếm số thành phần

liên thông được gán là 0

Bước 2: Chọn một đỉnh i bất kỳ chưa được duyệt, sử dụng một hàm Visit() để duyệt

đỉnh i và tất cả các đỉnh j có nối với đỉnh i Trong quá trình duyệt, ta sẽ đánh dấu các

đỉnh này là đã được duyệt để sau này không xét trở lại nữa Kết thúc lần duyệt này, ta được một miền liên thông

Để có thể dễ dàng in lại từng thành phần liên thông, ta sử dụng nhãn đánh dấu cho

Bước 3: Tăng biến đếm số thành phần liên thông

Bước 4: Nếu số thành phần liên thông là 1, in ra đồ thị liên thông

Ngược lại, in ra số thành phần liên thông

II CÀI ĐẶT

// nhãn cho các đỉnh, 0 là chưa duyệt

Trang 9

// 1, 2, … là đã duyệt và đỉnh thuộc miền liên thông tương ứng

int visited[MAX];

int nconnect;

void solve(GRAPH& g) {

// kh ởi tạo nhãn cho tất cả các đỉnh là chưa duyệt

// đặt số miền liên thông ban đầu la 0

if (visited[i] == 0) { visit(g, i, nconnect);

nconnect++;

} }

void visit(GRAPH& g, int i, int nconnect) {

// gán nhãn nconnect cho đỉnh i visited[i] = nconnect;

// duy ệt các đỉnh j chưa được duyệt và có nối với i

III BÀI T ẬP

phần liên thông

chỉ cách thêm vào G số cạnh tối thiểu để G liên thông

Trang 10

tấm ảnh nhị phân Một đối tượng được xác định gồm các điểm đen liên thông bốn với

Dữ liệu: Dữ liệu vào từ file văn bản BITMAP.INP

chiều dài và chiều rộng của tấm ảnh

Kết quả: Kết quả ghi ra file văn bản BITMAP.OUT như sau:

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 Vào một ngày làm việc bình thường , có một nhân viên bị nhiễm SARS , 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 người ta mới phát hiện ra người nhiễm bệnh SARS đầu tiên Khả năng lây lan của SARS rất nhanh chóng : một người nhiễm bệnh SARS 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 các bác sĩ kiểm tra xem cuối ngày hôm đó , có bao nhiêu người có

thể nhiễm bệnh và đó là những người nào để còn cách ly Người có tiếp xúc với người nhiễm bệnh được coi là người nhiễm bệnh

Dữ liệu: Dữ liệu vào từ file văn bản SARS.INP

Trang 11

- Dòng đầu tiên ghi 2 số tự nhiên N và K (1 < N <=250, 1<=K<=N) tương ứng số lượng người làm việc trong tòa nhà và số hiệu của nhân viên đã nhiễn SARS đ ầu tiên

người thứ I theo cách sau : số đầu tiên J của dòng là tổng số nhân viên đã gặp người thứ I, tiếp theo là J số tự nhiên lần lượt là số hiệ u của các nhân viên đó Nếu J=0 có nghĩa là không ai đã tiếp xúc với người I

Kết quả: Kết quả ghi ra file văn bản SARS.OUT như sau:

sách cần được sắp xếp theo thứ tự tăng dần của số hiệu nhân viên

Trong các file dữ liệu và kết quả , các số trên cùng một dòng cách nhau ít nhất một dấu cách

Trang 12

Chương 3: Cây và cây bao trùm

I NH ẮC LẠI LÝ THUYẾT

Cây là đồ thị vô hướng, liên thông và không có chu trình đơn Cây bao trùm của đồ thị vô hướng liên thông G là cây chứa tất cả các đỉnh của G Cây bao trùm của đồ thị có thể được xác định bằng các cách sau:

Xác định cây bao trùm bằng thuật toán hợp nhất: Xuất phát từ tập T chứa n đỉnh của

G và không chứa cạnh nào, ta lần lượt thêm dần các cạnh trong G vào T sao cho không tạo ra chu trình Đến khi đạt n-1 cạnh, T là cây bao trùm của G

Xác định cây bao trùm bằng thuật toán tìm kiếm: Áp dụng thuật giải duyệt đồ thị,

II CÀI ĐẶT

từ đỉnh i và previous[i] chính là cây bao trùm cần tìm

III BÀI T ẬP

rìa lâu đài, do v ậy, nếu thoát ra được rìa lâu đài thì coi như đã thoát hi ểm Để nguỵ trang, người ta cho đào nhiều nhánh hầm cụt và cửa vào giả Ngoài ra, để tăng khả năng thoát hiểm, người ta còn xây dựng các đường hầm giao nhau tại một số vị trí Để

(ô ở góc trên trái có toạ độ (0, 0)) 2 ô chỉ có thể thông nhau nếu chúng có chung cạnh

Trang 13

Dữ liệu nhập vào từ tập tin văn bản DUONGHAM.IN gồm:

trung tâm)

• N dòng tiếp theo, mỗi dòng chứa n số là các số ở các vị trí tương ứng trên hoạ đồ

phải đi qua theo đúng trình tự của một cách thoát hiểm

Trang 14

Chương 4: Thuật toán Prim

I NH ẮC LẠI LÝ THUYẾT

duyệt đồ thị đã đề cập ở bài 3, các bước thực hiện của thuật toán như sau:

Bước 1: Đánh dấu tất cả các đỉnh của đồ thị là chưa duyệt

Bước 2: Chọn một đỉnh bất kỳ làm gốc của cây bao trùm, để thống nhất ta chọn đỉnh 0, đánh

dấu đỉnh này là đã duyệt

Bước 3: Chọn cạnh (x, y) có trọng số nhỏ nhất nối giữa vùng đỉnh đã duyệt và vùng đỉnh

chưa duyệt (x được đánh dấu là đã duyệt và y được đánh dấu là chưa duyệt) Nếu không tìm được một cạnh nào như thế, dừng thuật toán

Bước 4: Cập nhật cạnh (x, y) vào cây bao trùm

Bước 5: Đánh dấu đỉnh y là đã duyệt

Bước 6: Quay lại bước 3

Trang 15

dụng PriorityQueue

Trang 16

Chương 5: Cây bao trùm nhỏ nhất & Thuật toán Kruskal

I NH ẮC LẠI LÝ THUYẾT

đề cập ở bài 3, các bước chi tiết của thuật toán như sau:

Bước 1: Tạo danh sách cạnh edges Đánh dấu tất cả các cạnh là chưa duyệt Bước 2: Sắp xếp lại các cạnh theo thứ tự trọng số tăng dần

Bước 3: Khởi tạo nhãn root, với root[i] = i (root[i] biểu diễn đỉnh gốc của i

trong cây chứa i)

Bước 4: Chọn ra cạnh e = (x,y) nhỏ nhất chưa duyệt trong danh sách cạnh

Bước 5: Đánh dấu cạnh e là đã duyệt Nếu nhãn của root[e.x] và root[e.y]

làm phát sinh chu trình), quay lại bước 4

Bước 6: Cập nhật cạnh e vào cây bao trùm, nếu số cạnh đã chọn n-1, dừng

int i = edge[k][i];

int j = edge[k][j];

// ki ểm tra khi thêm cạnh nào vào cây có tạo ra chu trình

Trang 17

int rj = j; while (root[rj] != -1) rj = root[rj];

này

khi xử lí thuật toán

Trang 18

Chương 6: Tìm đường trong đồ thị

I NH ẮC LẠI LÝ THUYẾT

cung đi qua)

cụ thể, ta phải thực hiện lại việc lưu vết (lưu đỉnh cha)

Để tìm đường trong đồ thị, ta có thể sử dụng phương pháp duyệt đồ thị theo chiều sâu

thông đã nêu ở chương 2) Nếu cài đặt bằng phương pháp khử đệ quy, điểm khác biệt

stackcount = 1; // ch ỉ có một phần tử trong ngăn xếp

// duy ệt tất cả các đỉnh trong ngăn xếp

while (stackcount > 0) {

// đưa j vào ngăn xếp

Trang 19

stackcount++;

// gán đường đi cho j

previous[j] = i;

… }

} }

// duy ệt tất cả các đỉnh trong hàng đợi

while (queueindex < queuecount) {

// đưa j vào hàng đợi

} }

III BÀI T ẬP

đường đi từ đỉnh I đến đỉnh J bất kì nhập từ bàn phím sử dụng thuật toán tìm kiếm theo chiều sâu

đường đi từ đỉnh I đến đỉnh J bất kì nhập từ bàn phím sử dụng thuật toán tìm kiếm theo chiều rộng

một phần tử trong đó)

Trang 20

Chương 7: Tìm đường trong đồ thị - Thuật toán Dijkstra

I NH ẮC LẠI LÝ THUYẾT

Thuật toán Dijkstra có thể mô tả như sau:

nguồn s đến một đỉnh u nào đó thuộc S, rồi đi theo cạnh nối u-v

Trong các đỉnh ngoài S, chúng ta chọn đỉnh u có nhãn d[u] bé nhất, bổ sung vào tập S

hợp với định nghĩa

đi ngắn nhất đến một đỉnh đích t, thì chúng ta dừng lại khi đỉnh t được bổ sung vào tập S

2 Cài đặt Dijkstra sử dụng Heap

ởi tạo tập S chỉ chứa đỉnh ban đầu s;

Trang 21

for (mỗi đỉnh v thuộc G) { D[v] = C(s, v);

} D[s] = 0;

}

III BÀI T ẬP

1 Cài đặt thuật toán Dijkstra

G quen nhau đã lâu Không bi ết từ lúc nào cả hai đã tuân thủ một qui ước: mỗi chiều

đi học về cần gặp nhau tại một địa điểm do G chọn (có thể là rạp chiếu bóng, quán kem hay đơn giản chỉ là góc phố quen) sau đó về thẳng nhà của mỗi người Nhiệm vụ

sau đó về nhà – dĩ nhiên, B và M đang học 2 trường khác nhau và ở 2 nhà cũng khác

đánh số từ 1 đến N, trường NAM là địa điểm U, trường NỮ là địa điểm V, nhà B là địa điểm Z, nhà G là địa điểm T, điểm hẹn là địa điểm X B cũng đã kh ảo sát và ghi

điểm không có đường nối trực tiếp Bạn hãy giúp B lập trình để có thể xác định đường

đi cho B và M sao cho tổng quãng đư ờng cả hai người phải đi qua là bé nhất – vì B chưa học LTĐT

- Dòng đầu tiên ghi số nguyên N là số địa điểm của thành phố A (N <= 100)

cách đoạn đường nối từ I đến J và được ghi là 0 nếu không có đường nối

- Dòng cuối cùng ghi 5 số U, V, Z, T, X

Trang 22

Chương 8: Tìm đường trong đồ thị - Thuật toán Floyd

I NH ẮC LẠI LÝ THUYẾT

Cho đơn đồ thị có hướng, có trọng số G=(V,E) với n đỉnh và m cạnh, Ma trận trọng số C[u,v] Bài toán đặt ra là tính tất cả các d(u,v) là khoảng cách nhỏ nhất từ u đến v

tính lại các C[u,v] thành đường đi ngắn nhất từ u đến v theo công thức:

C[u,v] := min (C[u,v], C[u,k] C[k,v]) với mọi đỉnh k xét từ 1 đến n

đến v thì ta ghi nhận lại đường đi ngắn nhất (hiện có) là đường đi qua k

int **Cost; Cost = new int *[g.n];

for (i = 0; i < gr.nV; i++) Cost[i] = new int [g.n];

int **Previous; Previous = new int *[g.n];

for (i = 0; i < g.n; i++) Previous[i] = new int [g.n];

for (j = 0; j < g.n; j++)

if (Cost[i][j] > Cost[i][k] + Cost[k][j]){

Cost[i][j] = Cost[i][k] + Cost[k][j];

Previous[i][j] = Previous[k][j];

} }

ẬP

Trang 23

1 Cài đặt thuật toán Floyd

tới các đỉnh còn lại là ngắn nhất

quanh điểm O sao cho chu vi hàng rào là nhỏ nhất (O không nằm trên cạnh của hàng rào)

Ngày đăng: 24/05/2017, 07:19

TỪ KHÓA LIÊN QUAN

w