Bài toán "Thanh tra giao thông"

Một phần của tài liệu các thuật toán về đường đi và chu trình euler và ứng dụng (Trang 46 - 65)

Bài toán: Một đội tuần tra giao thông xuất phát từ trụ sở chính, họ phải tuần tra trên tất cả các con đường của một khu vực đội quản lý rồi quay trở về trụ sở. Bài toán được đưa về dạng tìm chu trình Euler với đồ thị G là khu vực quản lý, với các cạnh là những con đường, các đỉnh là các nút giao thông, đỉnh xuất phát là trụ sở chính.

Số hóa bởi Trung tâm Học liệu http://www.lrc-tnu.edu.vn/

Bản đồ khu vực quản lý mà đội thanh tra phải đi như Hình 3.4

Hình 3.4 Bản đồ khu vực thanh tra

Từ Hình 3.4 ta có đồ thị G với các đỉnh là các ngã 2, ngã 3, ngã tư và các cạnh là đường đi như Hình 3.5

Số hóa bởi Trung tâm Học liệu http://www.lrc-tnu.edu.vn/ Hình 3.5 Đồ thị biểu diễn bản đồ ở hình 3.4

Bài toán trở thành tìm 1 chu trình Euler của đồ thị G liên thông.

Rõ ràng nếu G là một đồ thị Euler (tất cả các đỉnh đều bậc chẵn) thì việc tìm hành trình của đội thanh tra chính là tìm 1 chu trình Euler. Nhưng ta nhận thấy đồ thị G có 31 đỉnh, trong đó : 23 1 2 3 4 5 8 7 6 9 10 11 13 16 17 12 14 15 19 20 21 22 25 26 27 28 24 18 29 30 31

Số hóa bởi Trung tâm Học liệu http://www.lrc-tnu.edu.vn/

- Tập đỉnh bậc chẵn V0={1, 5, 7, 10, 11, 13, 16, 17, 20, 21, 22, 23, 24, 25, 31} (15 đỉnh)

- Tập đỉnh bậc lẻ V1={2, 3, 4, 6, 8, 9, 12, 14, 15, 18, 19, 26, 27, 28, 29, 30} (16 đỉnh)

Khi đó một hành trình phải đi qua ít nhất 2 lần một số cạnh nào đó. Ta quy ước hành trình T trong đồ thị Euler GT có được bằng cách vẽ thêm một cạnh song song với mỡi cạnh mà hình trình T đi qua 2 lần là chu trình Euler cần phải tìm.

Trước hết để có được đồ thị GT là đồ thị Euler ta phải đưa tập đỉnh bậc lẻ V1 thành bậc chẵn. Công việc cần làm là phân hoạch các đỉnh trong tập V1 thành từng cặp sao cho tổng số cạnh thêm vào là ít nhất (số đường đi ngắn nhất).

Tính đường đi ngắn nhất giữa các cặp đỉnh lẻ :

Tập hợp V1 có tất cả 16 đỉnh số cặp sẽ là tổ hợp chập 2 của 16 bằng 120 Trong đó có chỉ số cặp đỉnh và Dmin là số cạnh phải nối giữa hai đỉnh

Số hóa bởi Trung tâm Học liệu http://www.lrc-tnu.edu.vn/ Cặp đỉnh 2, 3 2, 4 2, 6 2, 8 2, 9 2, 12 2, 14 2, 15 2, 18 2, 19 Dmin 1 2 2 4 3 1 3 4 2 3 Cặp đỉnh 2, 26 2, 27 2, 28 2, 29 2, 30 3, 4 3, 6 3, 8 3, 9 3, 12 Dmin 4 5 6 3 4 1 1 3 2 2 Cặp đỉnh 3, 14 3, 15 3, 18 3, 19 3, 26 3, 27 3, 28 3, 29 3, 30 4, 6 Dmin 4 5 3 4 5 6 6 4 5 2 Cặp đỉnh 4, 8 4, 9 4, 12 4, 14 4, 15 4, 18 4, 19 4, 26 4, 27 4, 28 Dmin 2 3 3 5 4 4 5 6 6 5 Cặp đỉnh 4, 29 4, 30 6, 8 6, 9 6, 12 6, 14 6, 15 6, 18 6, 19 6, 26 Dmin 5 6 2 1 3 3 4 4 3 5 Cặp đỉnh 6, 27 6, 28 6, 29 6, 30 8, 9 8, 12 8, 14 8, 15 8, 18 8, 19 Dmin 6 5 5 5 3 5 5 4 6 5 Cặp đỉnh 8, 26 8, 27 8, 28 8, 29 8, 30 9, 12 9, 14 9, 15 9, 18 9, 19 Dmin 7 6 5 7 7 2 2 3 4 2 Cặp đỉnh 9, 26 9, 27 9, 28 9, 29 9, 30 12, 14 12, 15 12, 18 12, 19 12, 26 Dmin 4 5 4 4 4 2 3 2 2 3 Cặp đỉnh 12, 27 12, 28 12, 29 12, 30 14, 15 14, 18 14, 19 14, 26 14, 27 14, 28 Dmin 4 5 2 3 1 4 2 2 3 4 Cặp đỉnh 14, 29 14, 30 15, 18 15, 19 15, 26 15, 27 15, 28 15, 29 15, 30 18, 19 Dmin 4 4 5 3 3 2 3 5 5 3 Cặp đỉnh 18, 26 18, 27 18, 28 18, 29 18, 30 19, 26 19, 27 19, 28 19, 29 19, 30 Dmin 3 4 4 1 2 2 3 4 3 2 Cặp đỉnh 26, 27 26, 28 26, 29 26, 30 27, 28 27, 29 27, 30 28, 29 28, 30 29, 30 Dmin 1 2 3 2 1 4 3 3 2 1

Bảng 3.2 Số cạnh nối thêm giữa các cặp đỉnh bậc lẻ

Từ Bảng 3.2 sử dụng phương pháp tham lam lần lượt chọn các cặp đỉnh sẽ nối có giá trị cạnh thêm là nhỏ nhất, chọn cho đến khi đủ tất cả các đỉnh bậc lẻ của đồ thị như sau :

Số hóa bởi Trung tâm Học liệu http://www.lrc-tnu.edu.vn/

STT Cặp đỉnh chọn Dmin Cạnh nối thêm

1 2, 3 1 (2, 3) 2 6, 9 1 (6, 9) 3 14 15 1 (14, 15) 4 18 29 1 (18, 29) 5 26 27 1 (26, 27) 6 4 8 2 (4, 5) ; (5, 8) 7 12 19 2 (12, 13) ; (13, 19) 8 28 30 2 (28, 31) ; (31, 30) Bảng 3.3 Cách chọn cặp đỉnh bậc lẻ và số cạnh nối thêm

Số hóa bởi Trung tâm Học liệu http://www.lrc-tnu.edu.vn/

Đồ thị GT sau khi thêm các cạnh:

Hình 3.6 Đồ thị GT có được khi thêm cạnh (các nét đứt là các cạnh nối thêm)

Khi đó đồ thị GT là một đồ thị Euler (tất cả các đỉnh đều bậc chẵn), áp dụng thuật toán Hierholzer với đỉnh xuất phát là đỉnh 3 (do trụ sở là nằm tại đỉnh 3)

23 1 2 3 4 5 8 7 6 9 10 11 13 16 17 12 14 15 19 20 21 22 25 26 27 28 24 18 29 30 31

Số hóa bởi Trung tâm Học liệu http://www.lrc-tnu.edu.vn/ Sô đỉnh: 31 Số cạnh: 59 Số đỉnh bậc lẻ là: 0 Có chu trình Euler: 3 2 12 13 19 25 30 31 28 31 30 29 18 29 24 25 26 27 28 22 23 17 16 22 21 27 26 20 21 15 14 20 19 13 14 15 16 10 11 8 5 4 7 10 9 6 9 13 12 24 18 1 2 3 6 7 8 5 4 3

Bảng 3.4 Chu trình Euler tìm được với đồ thị GT

Số hóa bởi Trung tâm Học liệu http://www.lrc-tnu.edu.vn/

KẾT LUẬN VÀ HƢỚNG PHÁT TRIỂN Các kết quả đạt đƣợc:

Đề tài đã tìm hiểu một số thuật toán tìm chu trình Euler, cài đặt thuật toán và tổ chức lại dữ liệu để tăng tốc độ tính toán của chương trình. (adsbygoogle = window.adsbygoogle || []).push({});

Từ đó, vận dụng thuật toán vào ứng dụng cụ thể để giải quyết bài toán thanh tra giao thông. Đưa ra hành trình cho đội tuần tra một cách tốt nhất có thể.

Cài đặt chương trình so sánh tốc độ tính toán sau khi tổ chức dữ liệu với đồ thị có số đỉnh lớn.

Đề tài có tiếp tục phát triển để đem lại đáp ứng những yêu cầu thực tế.

Hƣớng phát triển của đề tài:

Cách tổ chức dữ liệu giúp tăng tốc độ tính toán, từ đó có thể áp dụng vào những ứng dụng có dữ liệu lớn, thí dụ như tìm đường đi cho các gói tin trên mạng…

Ngoài ra từ thuật toán tìm chu trình Euler cơ bản có thể áp dụng giải quyết những biến thể của bài toán, như tìm các chu trình Euler trong một đồ thị, hay tìm chu trình Euler của đồ thị con từ đó có thể giải quyết những bài toán liên quan như chuyển hàng từ kho đến các địa điểm …

Số hóa bởi Trung tâm Học liệu http://www.lrc-tnu.edu.vn/

TÀI LIỆU THAM KHẢO Tiếng Việt

[1] Kenneth H.Rosen, Toán rời rạc ứng dụng trong Tin học (Bản dịch), NXB Khoa học và Kỹ thuật, 2000.

[2] Nguyễn Đức Nghĩa, Nguyễn Tô Thành, Toán rời rạc, NXB Đại học Quốc Gia Hà Nội, 2003.

[3] Nguyễn Xuân Huy, Sáng tạo trong thuật toán và lập trình, NXB Thông tin và truyền thông 2011.

[4] Lê Minh Hoàng, Giải thuật & lập trình, Đại học sư phạm Hà Nội 1999 - 2002.

Tiếng Anh

[5] S. Pemmaraju, and S. Skiena, Computational Discrete Mathematics: Combinatorics and Graph Theory with Mathematica. Cambridge, England: Cambridge University Press, 2003.

Số hóa bởi Trung tâm Học liệu http://www.lrc-tnu.edu.vn/

PHỤ LỤC

Chương trình C++

Chƣơng trình thuật toán tìm chu trình Euler (phƣơng án 1)

#include <iostream> #include <fstream> using namespace std; const int MN = 500; typedef int mi1[MN]; typedef mi1 mi2[MN];

mi2 c; // ma tran ke c[i][j] = c[j][i] mi1 bac;

mi1 r; // cac dinh chua duyet int ir; // index for r

mi1 p; // Path

int k; // ngon stack: chi so tien int last; // chi lui

int n; // so dinh int m; // so canh

int dinhLe1, dinhLe2; // 2 dinh le, neu co // Hiển thị mảng c[d..c] với thông báo msg

void Print(mi1 a, int d, int c, char *msg = ""){ cout << msg;

for (int i = d; i <= c; ++i){ cout << a[i] << " ";

} }

// Hiển thị mảng 2 chiều với thông báo

void Print(mi2 a, int d, int c, char *msg = ""){ cout << msg; (adsbygoogle = window.adsbygoogle || []).push({});

for (int i = d; i <= c; ++i) Print(a[i],d,c,"\n "); }

/* Đọc theo cạnh

Ma trận kề c[i][j] = so canh (i,j) (co the nhieu canh noi dinh)*/

void ReadData(const char * fn){ int i, j, k;

Số hóa bởi Trung tâm Học liệu http://www.lrc-tnu.edu.vn/

ifstream f(fn); f >> n >> m;

cout << "\n So dinh n = " << n;

cout << " So canh m = " << m << endl;

memset(c,0,sizeof(c)); memset(bac,0,sizeof(bac)); for (k = 1; k <= m; ++k){

f >> i >> j; // doc canh (i,j)

++c[i][j]; ++c[j][i]; // ghi ma tran ke ++bac[i]; ++bac[j];

}

f.close(); }

/* Đếm số đỉnh lẻ

Ghi nhận 2 đỉnh lẻ 1 và 2, nếu có để tìm đường đi Euler*/ int SoDinhLe(){ int i, j, le = 0; le = 0; // so dinh le dinhLe1 = dinhLe2 = 0; cout << " Bac: "; for (i = 1; i <= n; ++i){ cout << bac[i] << " ";

if (bac[i] & 1) { // i la dinh le ++le;

dinhLe1 = dinhLe2; // Ghi nhan 2 dinh le dinhLe2 = i;

} }

cout << "\n So dinh le " << le; if (le > 0) {

cout << ": " << dinhLe1;

if (le > 1) cout << " , " << dinhLe2; }

return le; }

// tìm đỉnh j sau đỉnh thỏa điều kiện // cạnh (i,j) chưa thăm

int TimDinhTiep(int i){

if (bac[i] == 0) return 0; for (int j = 1; j <= n; ++j)

Số hóa bởi Trung tâm Học liệu http://www.lrc-tnu.edu.vn/

if (c[i][j] > 0) return j; }

// Tiến từ đỉnh p[k]

// Ghi kết quả vào stack p[k+1..] void Tien(){

int j; (adsbygoogle = window.adsbygoogle || []).push({});

cout << " \n Tien: " << p[k]; while(1){

j = TimDinhTiep(p[k]);

if (j == 0) return; // khong tim duoc canh p[k]--> j

p[++k] = j; // Them dinh j vao Path

cout << " " << p[k]; // co canh p[k]-->j --c[p[k-1]][p[k]]; --c[p[k]][p[k-1]]; // giam so cung da tham --bac[p[k]]; --bac[p[k-1]]; } }

// Đổ stack p: Quay lui từ e đến k để tìm đỉnh p[i] // là điểm đầu của 1 đỉnh chưa thăm

void Lui(){ int i,j; cout << " \n Lui: "; while(1){ cout << " " << p[k]; p[--last] = p[k]; --k; if (bac[p[k]] > 0) return; } }

// Tìm đường đi Euler nếu đồ thị có đúng 2 đỉnh lẻ

// Tìm đường đi Euler nếu đồ thị không có đỉnh lẻ void Euler(int v){ k = 1; p[k] = v; last = m+2; while(1){ Tien(); if (k+1 == last) break; Lui(); }

Số hóa bởi Trung tâm Học liệu http://www.lrc-tnu.edu.vn/

}

// Sinh ngẫu nhiên đồ thị với n đỉnh // mode = 0: mọi đỉnh đề chẵn

// mode = 1: 2 đỉnh lẻ

bool Gen(const char * fn, int n, int mode = 0) { int k,i,j,t,m;

srand(time(NULL));

m = (mode == 1) ? (n-1)+(n-2) : (n-1)+(n-2)+1; if (m >= MN) {

cout << "\n so canh phai < " << MN; return 0;

}

for (i = 1; i <= n; ++i) p[i] = i; ofstream f(fn);

f << n << " " << m;

for (k = 0; k < 4; ++k)

for (i = 1; i < n; ++i) { // Xáo trộn ngẫu nhiên

j = rand() % n + 1;

t = p[1]; p[1] = p[j]; p[j] = t; }

for (i = 1; i < n; ++i){ // Ghi file

f << endl << p[i] << " " << p[i+1]; }

for (i = 1; i+2 <= n; i += 2){ // Ghi file f << endl << p[i] << " " << p[i+2]; }

for (i = 2; i+2 <= n; i += 2){ // Ghi file f << endl << p[i] << " " << p[i+2]; } if (mode == 0) f << endl << p[2] << " " << p[n- 1]; // them 1 canh f.close(); return 1; }

void Run(const char * fn){

ReadData(fn); // doc du lieu int e; (adsbygoogle = window.adsbygoogle || []).push({});

Số hóa bởi Trung tâm Học liệu http://www.lrc-tnu.edu.vn/

if (e > 2) cout << endl << "\n 0: Ko co duong Euler. ";

else if (e == 0) {

cout << " Co chu trinh Euler. ";

Euler(1); // Tim chu trinh Euler tu dinh 1 }

else {

cout << " Chi co duong Euler. ";

Euler(dinhLe1);/* Tim chu trinh Euler tu dinh le thu nhat */

} }

main(){

const char * fn = "Euler.inp"; // if (Gen(fn,250,0)) Run(fn); cout << endl << "\n\n F I N I ! \n "; system("pause"); return 0; }

Chƣơng trình cho phƣơng án tổ chức lại dữ liệu:

// Duong va chu trinh Euler. Phuong an 2 #include <iostream>

#include <fstream> #include <time.h> using namespace std; const int MN = 125001; typedef int mi1[MN]; typedef struct Elem {

int Vert; // dinh den

Elem * Pred; // tro truoc Elem * Next; // Tro sau

Elem * Adj; // Tro toi dinh thuoc cung canh };

Elem ** cc; mi1 p; // Path

Số hóa bởi Trung tâm Học liệu http://www.lrc-tnu.edu.vn/

int k; // ngon p int last; // cuoi p int n; // so dinh int m; // so canh

int dinhLe1, dinhLe2; /* 2 dinh le, neu co cho trung hop duong Euler */

int bac[MN];

// Hien thi mang c[d..c] voi thong bao msg

void Print(mi1 a, int d, int c, char *msg = ""){ cout << msg;

for (int i = d; i <= c; ++i){ cout << a[i] << " ";

} }

// Cap phat va khoi tri cho mot dinh ung voi canh (u,v)

Elem * NewElem(int v, Elem * adj, Elem * nxt) { Elem * p = new Elem;

p->Vert = v; // so hieu dinh phai p->Pred = p; // tro truoc

p->Adj = adj; // tro toi dinh u (adsbygoogle = window.adsbygoogle || []).push({});

p->Next = nxt; // tro toi phan tu sau return p;

}

// Cap phat 2 dinh (i,j) va (j,i)

// Them vao dau 2 danh sach cc[i] va cc[j] void Add(int i, int j) {

Elem *p = NewElem(j,NULL,cc[i]); Elem *q = NewElem(i,p,cc[j]); cc[i]->Pred = p; cc[j]->Pred = q; cc[i] = p; cc[j] = q; p->Adj = q; }

// Xoa phan tu dau danh sach i // va phan tu ke Adj

void Del(int i){

Số hóa bởi Trung tâm Học liệu http://www.lrc-tnu.edu.vn/

Elem *q = p->Adj; // dau phai int j = p->Vert;

if (j == 0) return; /* Gap phan tu dem, danh sach cc[i] rong */

cc[i] = p->Next; cc[i]->Pred = cc[i]; delete p;

if (q->Pred == q) { // q la dau danh sach j cc[j] = q->Next;

cc[j]->Pred = cc[j]; delete q;

return; }

// q o giua danh sach p = q->Pred; // p truoc q

p->Next = q->Next; // thao q khoi danh sach cc[j] (q->Next)->Pred = p; delete q; } // Xoa toan bo cc void DelAll(){ int i; Elem *p, *q; for (i = 0; i <= n; ++i) { p = cc[i]; while (p != NULL) { q = p; p = p->Next; delete q; } } delete [] cc; }

// Doc theo canh (co the nhieu canh noi 2 dinh i j) void ReadData(const char * fn){

int i, j, k; ifstream f(fn); f >> n >> m;

cout << "\n So dinh n = " << n; cout << " So canh m = " << m;

Số hóa bởi Trung tâm Học liệu http://www.lrc-tnu.edu.vn/

memset(bac,0,sizeof(bac)); cc = new Elem*[n+1];

// Khoi tao cac phan tu

for (i = 0; i <= n; ++i) cc[i] = NewElem(0,NULL,NULL);

for (k = 1; k <= m; ++k){

f >> i >> j; // doc canh (i,j) Add(i,j); ++bac[i]; ++bac[j]; }

f.close(); }

/* Dem so dinh le

Ghi nhan 2 dinh le 1 va 2, neu co de tim duong Euler */ int SoDinhLe(){ int i, j, le = 0; dinhLe1 = dinhLe2 = 0; cout << "\n Bac: "; for (i = 1; i <= n; ++i){ cout << " " << bac[i];

if (bac[i] & 1) { // i la dinh le ++le;

dinhLe1 = dinhLe2; // Ghi nhan 2 dinh le dinhLe2 = i; (adsbygoogle = window.adsbygoogle || []).push({});

} }

cout << "\n So dinh le " << le; if (le > 0) {

cout << ": " << dinhLe1;

if (le > 1) cout << ", " << dinhLe2; }

return le; }

// tim dinh j sau dinh i thoa dieu kien

Một phần của tài liệu các thuật toán về đường đi và chu trình euler và ứng dụng (Trang 46 - 65)