1. Trang chủ
  2. » Luận Văn - Báo Cáo

Tìm đường đi ngắn nhất giữa 2diểm trong 1 mê cung

20 2,3K 17

Đ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 20
Dung lượng 541 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Các cấu trúc dữ liệu được triển khai bằng cách sử dụng các kiểu dữ liệu, các tham chiếu và các phép toán trên đó được cung cấp bởi một ngôn ngữ lập trình.. Để có thể đi sâu và nắm vững k

Trang 1

LỜI MỞ ĐẦU Trong khoa học máy tính, cấu trúc dữ liệu là một cách lưu dữ liệu trong

máy tính sao cho nó có thể được sử dụng một cách hiệu quả Thông thường, một cấu trúc dữ liệu được chọn cẩn thận sẽ cho phép thực hiện thuật toán hiệu quả hơn Việc chọn cấu trúc dữ liệu thường bắt đầu từ chọn một cấu trúc dữ liệu trừu tượng Một cấu trúc dữ liệu được thiết kế tốt cho phép thực hiện nhiều phép toán, sử dụng càng ít tài nguyên, thời gian xử lý và không gian bộ nhớ càng tốt Các cấu trúc dữ liệu được triển khai bằng cách sử dụng các kiểu dữ liệu, các tham chiếu và các phép toán trên đó được cung cấp bởi một ngôn ngữ lập trình Mỗi loại cấu trúc dữ liệu phù hợp với một vài loại ứng dụng khác nhau, một số cấu trúc dữ liệu dành cho những công việc đặc biệt

Để có thể đi sâu và nắm vững kiến thức đã thu nhận được trong quá trình

học môn Cấu Trúc Dữ Liệu, em chọn đề tài “Tìm đường đi ngắn nhất giữa 2 diểm trong 1 mê cung” để tìm hiểu và nghiên cứu.

Do kiến thức còn hạn chế, dù cố gắng hoàn thiện thật tốt nhưng chắc chắn

đồ án còn nhiều thiếu sót Em mong có được sự chỉ bảo, đóng góp ý kiến của thầy

và các bạn để chúng em có thể hiểu rõ và sâu sắc hơn về các cấu trúc dữ liệu và giải thuật ứng dụng trong tin học

Em xin chân thành cảm ơn!

Sinh viên thực hiện:

Trang 2

I GIỚI THIỆU VỀ ĐỀ TÀI:

1 Nội dung đề tài :

Trên bàn cờ ô vuông NxN các ô được đánh dấu 0 là các ô tự do, các ô được đánh dấu 1 là các ô có mìn Tìm đường đi ngắn nhất giữa 2 điểm

tự do A, B cho trước của “Mê cung”.

2 Tìm hiểu và phân tích đề tài :

a Truyền thuyết về mê cung :

Trong thần thoại Hi Lạp, có con quỷ Minoto rất hung dữ ở trong 1 hang sâu Đường đi vào hang là một mê cung, ai có can đảm vào diệt quỷ thì cũng không dễ gì lần được vào hang quỷ mà còn có thể bị lạc, không tìm được lối ra Người anh hùng Teze đã liều mình vào hang quỷ Để giúp anh, nàng Arian đã đưa cho Têze một cuộn chỉ và nàng cầm đầu mối Khi vào mê

lộ thì Têze kéo đần cuộn chỉ, đến lúc quay về thì chỉ cần cuốn chỉ lại để lần theo đó mà ra khỏi mê cung.

Chuyện thần thoại là vậy, còn mê cung thì đã hấp dẫn nhiều nhà kiến trúc, họa sĩ, thi sĩ trong hàng chục thế kỉ qua, Các nhà toán học cũng chú ý đến mê cung vì nó mang nhiều ý nghĩa sâu sắc liên quan đến nhiều ngành của toán học hiện đại Ngay trong cuộc sống thường ngày, chúng ta cũng thường gặp mê cung trong các bài toán đố vui :Tìm đường đi trong mê cung

b Phân tích đề tài :

Bài toán tìm đường đi giữa hai điểm trong mê cung được quy về bài toán tìm đường đi giữa hai điểm trong đồ thị Mê cung được quy đổi ra một

ma trận 0, 1 với quy định điểm 0 là các điểm có thể đi qua, các điểm 1 là chướng ngại vật và không thể đi qua Có nhiều thuật toán trên đồ thị được xây dựng để duyệt tất cả các đỉnh của đồ thị sao cho mỗi đỉnh được viếng thăm đúng một lần Những thuật toán như vậy được gọi là thuật toán tìm kiếm trên đồ thị Đối với bài toán này,chúng ta có thể sử dụng một trong hai thuật toán tìm kiếm cơ bản, đó là duyệt theo chiều sâu DFS (Depth First Search) hoặc duyệt theo chiều rộng BFS (Breath First Search)

Trang 3

c Tìm hiểu các giải thuât có thể giải quyết bài toán :

i)Thuật toán tìm kiếm theo chiều sâu (DFS) :

Tư tưởng cơ bản của thuật toán tìm kiếm theo chiều sâu là bắt đầu tại một đỉnh v0 nào đó, chọn một đỉnh u bất kỳ kề với v0 và lấy nó làm đỉnh duyệt tiếp theo Cách duyệt tiếp theo được thực hiện tương tự như đối với đỉnh v0

với đỉnh bắt đầu là u

Để kiểm tra việc duyệt mỗi đỉnh đúng một lần, chúng ta sử dụng một mảng chuaxet[] gồm n phần tử (tương ứng với n đỉnh), nếu đỉnh thứ i đã được duyệt, phần tử tương ứng trong mảng chuaxet[] có giá trị FALSE Ngược lại, nếu đỉnh chưa được duyệt, phần tử tương ứng trong mảng có giá trị TRUE Thuật toán có thể được mô tả bằng thủ tục đệ qui DFS () trong đó: chuaxet -

là mảng các giá trị logic được thiết lập giá trị TRUE

void DFS( int v){

Thăm_Đỉnh(v); chuaxet[v]:= FALSE;

for ( u ke(v) ) { ∈

if (chuaxet[u] ) DFS(u);

}

}

Thủ tục DFS() sẽ thăm tất cả các đỉnh cùng thành phần liên thông với v mỗi đỉnh đúng một lần Để đảm bảo duyệt tất cả các đỉnh của đồ thị (có thể có nhiều thành phần liên thông), chúng ta chỉ cần thực hiện duyệt như sau:

{

for (i=1; i≤ n ; i++)

chuaxet[i]:= TRUE; /* thiết lập giá trị ban đầu cho mảng chuaxet[]*/ for (i=1; i≤ n ; i++)

if (chuaxet[i] )

DFS( i);

}

*)Chú ý: Thuật toán tìm kiếm theo chiều sâu dễ dàng áp dụng cho đồ thị có

hướng Đối với đồ thị vô hướng, chúng ta chỉ cần thay các cạnh vô hướng bằng các cung của đồ thị có hướng

Trang 4

Ví dụ:

Áp dụng thuật toán tìm kiếm theo chiều sâu với đồ thị trong hình sau:

Trang 5

ii)Thuật toán tìm kiếm theo chiều rộng (BFS):

Để ý rằng, với thuật toán tìm kiếm theo chiều sâu, đỉnh thăm càng muộn sẽ trở thành đỉnh sớm được duyệt xong Đó là kết quả tất yếu vì các đỉnh thăm được nạp vào stack trong thủ tục đệ qui Khác với thuật toán tìm kiếm theo chiều sâu, thuật toán tìm kiếm theo chiều rộng thay thế việc sử dụng stack bằng hàng đợi queue Trong thủ tục này, đỉnh được nạp vào hàng đợi đầu tiên là v, các đỉnh kề với v ( v1, v2, , vk) được nạp vào queue kế tiếp Quá trình duyệt tiếp theo được bắt đầu từ các đỉnh còn có mặt trong hàng đợi.

Để ghi nhận trạng thái duyệt các đỉnh của đồ thị, ta cũng vẫn sử dụng mảng chuaxet[] gồm n phần tử thiết lập giá trị ban đầu là TRUE Nếu đỉnh i của đồ thị đã được duyệt, giá trị chuaxet[i] sẽ nhận giá trị FALSE Thuật toán dừng khi hàng đợi rỗng Thủ tục BFS dưới đây thể hiện quá trình thực hiện của thuật toán:

void BFS(int u){

queue = φ;

u <= queue; /*nạp u vào hàng đợi*/

chuaxet[u] = false;/* đổi trạng thái của u*/

while (queue ≠ φ ) { /* duyệt tới khi nào hàng đợi rỗng*/

queue<=p; /*lấy p ra từ khỏi hàng đợi*/

Thăm_Đỉnh(p); /* duyệt xong đỉnh p*/

for (v ke(p) ) {/* đưa các đỉnh v kề với p nhưng chưa được xét vào hàng ∈ đợi*/

if (chuaxet[v] ) {

v<= queue; /*đưa v vào hàng đợi*/

chuaxet[v] = false;/* đổi trạng thái của v*/

}

}

} /* end while*/

}/* end BFS*/

Thủ tục BFS sẽ thăm tất cả các đỉnh dùng thành phần liên thông với u.

Để thăm tất cả các đỉnh của đồ thị, chúng ta chỉ cần thực hiện đoạn chương trình dưới đây:

{

for (u=1; u≤n; u++)

chuaxet[u] = TRUE;

for (u thuộc V )

if (chuaxet[u] )

Trang 6

BFS(u);

}

Ví dụ Áp dụng thuật toán tìm kiếm theo chiều rộng với đồ thị trong hình

sau:

Trang 7

iii) So sánh giữa 2 phương pháp:

Tìm kiếm chiều sâu và tìm kiếm chiều rộng đều là các phương pháp tìm kiếm có hệ thống và chắc chắn tìm ra lời giải Đối với đề tài này, nhóm chúng em sẽ sử dụng thuật toán tìm kiếm theo chiều sâu để giải quyết.

II.THUẬT TOÁN CƠ BẢN:

1.Xây dựng mê cung bằng ma trận kề:

Để có thể sử dụng được thuật toán đã chỉ ra ở trên, ta xây dựng mê cung bằng ma trận kề 0,1 Với quy ước ô 0 là ô có thể đi qua, còn ô 1 là ô không thể đi qua hay ô 1 là ô có mìn.

Ví dụ:

Ta xây dựng ma trận kề 5x5 cho “mê cung” sau đây:

ô trống : đi được

ô có dấu X: ô có mìn

Ma trận kề biểu diễn “mê cung” trên như sau:

X

X

X

X X

Trang 8

0 1 0 0 0

0 0 0 1 0

1 1 0 1 0

1 0 0 0 0

0 0 1 1 0

2.Áp dụng thuật toán tìm kiếm theo chiều sâu để tìm đường đi trong mê cung:

Bài toán áp dụng phương pháp tìm kiếm theo chiều sâu (Depth-First Search) như đã trình bày ở trên

- inx,iny: tọa độ điểm đầu, điểm xuất phát

- outx,outy : tọa độ của điểm cuối

- m,n: số hàng, số cột của ma trận kề a[i][j] như đã nhắc đến ở trên

Các giá trị này sẽ được nhập vào từ file input.txt

- Độ dài đường đi sẽ được lưu vào biến s

Như vậy đường đi ngắn nhất là đường đi có s nhỏ nhất Ở đây do không có trọng số khoảng cách giữa các điểm nên ta chỉ cần cộng thêm 1 vào s nếu đi tiếp được 1 ô.

- Ban đầu, do các ô (i,j) chưa được xét nên ta khởi tạo các gía trị cho

mảng chuaxet[i][j] = 1 Nếu sau khi đã xét xong ô (i,j) thì chuaxet[i][j]=0 Còn biến d chỉ ra đã tìm thấy đường đi hay chưa, nếu đã tim thấy thì d=1, ban đầu chưa có đường nào nên d=0

void chuanbi() {

for (i = 0; i < m; ++i) {

for (j = 0; j < n; ++j) {

chuaxet[i][j] = 1;

}

}

d = 0;

}

Trong phương pháp tìm kiếm theo chiều sâu, tại vi trí hiện hành, ta chọn một vị trí kế tiếp (trong tập các vị trí có thể biến đổi thành từ trạng thái hiện tại) làm trạng thái hiện hành cho đến lúc trạng thái hiện hành là trạng

Trang 9

thái đích Đối với một “mê cung” thì tại vị trí hiện hành ô (i,j) có thể đi được tối đa qua 4 ô xung quanh nó là các ô (i, j-1),(i, j+1),(i-1, j) và (i+1, j)

Ví dụ tại ô có tọa độ (2,2) thì ta có thể đi qua tối đa 4 ô xung quanh nó

là (2,1),(2,3),(1,2),(3,2)

Ta dùng biến k để xét các hướng đi.

k=1: xét ô bên trái ô hiện hành (i,j-1)

k=2: xét ô dưới ô hiện hành (i+1,j)

k=3: xét ô bên phải ô hiện hành (i,j+1)

k=4: xét ô trên ô hiện hành (i-1,j)

Nếu xung quanh ô dang xét có “mìn” hoặc đã có ô xét rồi thì ta loại bớt những ô đó đi và đi qua những ô có thể đi được.

- Hàm ok(int i,int j) xét xem các ô xung quanh ô (i,j) có thể đi được không Nếu đi được thì hàm ok(i,j) trả về giá trị 1 ngược lại trả về giá trị 0.

int ok(int i,int j)

{

if ((i<0)||(i>=m)||(j<0)||(j>=n)) return 0; điểm đang xét phải thuộc mê cung

if ((a[i][j]!=0)||(c[i][j]!=0)) return 0;

return 1;

}

Trong trường hợp tại trạng thái hiện hành, ta không thể biến đổi thành trạng thái kế tiếp thì ta sẽ quay lui (back-tracking) lại trạng thái trước trạng thái hiện hành (trạng thái biến đổi thành trạng thái hiện hành) để chọn đường khác Nếu ở trạng thái trước này mà cũng không thể biến đổi được nữa thì ta quay lui lại trạng thái trước nữa và cứ thế

- Hàm dfs(i, j) tìm và xét tất cả những điểm (i, j) trên đường đi

void dfs(int i,int j)

{

if ((i==outx-1)&&(j==outy-1)) d=1; //ktra neu da den diem cuoi cung // thì dừng, d=1 la da tim thay dường đi

else {

Trang 10

int k;

for (k=1;k<=4;++k) //tu diem dang xet ,xet cac diem xung

quanh:trai, dưới , phai , trên ok=1 tuc la di dc

if (ok(i+dx[k],j+dy[k])) {

c[i+dx[k]][j+dy[k]]=k; //c[i][j] chua gia tri cua k chinh la huong can di

dfs(i+dx[k],j+dy[k]); //xet diem tiep theo

}

}

}

Với dx[]={0,0,1,0,-1}

dy[]={0,-1,0,1,0} dùng để cộng (trừ) vào hàng, cột đang xét để xét điểm tiếp theo.

VD: k=1 dx[k]=0,dy[k]=-1 như vậy thì ta sẽ xét đến ô (i+0,j-1) tức là ô bên trái ô hiện hành…

Nếu đã quay lui đến trạng thái khởi đầu mà vẫn thất bại thì kết luận là không có lời giải (d=0)

Khi điểm hiện hành chính là điểm cần đến thì dừng dfs và ta thực hiện duyệt ngược trở lại từ điểm hiện hành đến điểm bắt đầu để tìm ra đường đi là kết quả cuối cùng cho bài toán.

void tim()

{

s=0;

x[0]=outx-1;

y[0]=outy-1;

Trang 11

j=outy-1;

int k; // bien k chi ra da di theo huong nao luc dau

while (c[i][j]!=-1) //tim theo thu tu nguoc

{

k=c[i][j];

i=i-dx[k];

j=j-dy[k];

s++;

x[s]=i; //mang x[s] va y[s] chua toa do cua nhung diem da di qua y[s]=j; //luc nay thi tim theo thu tu nguoc lai

}

}

- Kết quả được xuất ra bằng hàm xuất :

void xuat()

{

FILE *f;

f=fopen(out,"w");

if (d != 1)fprintf(f,"Noresult"); //neu d!=1 thi ko co duong di

if (d==1)

{

for (i=0;i<m;++i)

Trang 12

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

if (c[i][j]!=0) fprintf(f,"1 "); //in ra ma tran ban dau:o 1 la co min,o 0 la di dc else fprintf(f,"0 ");

fprintf(f,"\n");

}

fprintf(f,"%d\n",s);

for (i=s;i>=0;i ) fprintf(f,"%4d%4d\n",x[i],y[i]); //in ra toa do nhung diem da di qua

//tong cong co s diem

}

fclose(f);

}

III.CHƯƠNG TRÌNH:

Chương trình được viết và xử lí đồ họa bằng JAVA Sau đây là code của chương trình:

import java.io.FileInputStream;

import java.io.FileNotFoundException;

import java.io.IOException;

import java.util.logging.Level;

import java.util.logging.Logger;

import javax.swing.JOptionPane;

/*

* To change this template, choose Tools | Templates

* and open the template in the editor.

*/

Trang 13

* @author linh

*/

public class TimDuongDi {

private Result rs;

private Thread t;

private int m, n, i, j, d, s, max;

private int[][] matran = new int[50][50];

private int inx, iny, outx, outy;

private int[] x = new int[50];

private int[] y = new int[50];

private int[][] c = new int[100][100];

private int[][] chuaxet = new int[50][50];

private int[] dx = {0, -1, 0, 0, 1};

private int[] dy = {0, 0, 1, -1, 0};

private int str[] = new int[200];

public TimDuongDi() {

m = n = 10;

try {

FileInputStream f = new FileInputStream("out.txt");

int size, con, dem = 0;

try {

size = f.available();

for (i = 0; i < size; i++) {

con = f.read();

if (con != 0 && con != 32 && con != 10 && con != 13) {

str[dem] = con - 48;

dem++;

}

}

} catch (IOException ex) {

Logger.getLogger(TimDuongDi.class.getName()).log(Level.SEVERE, null, ex);

}

} catch (FileNotFoundException ex) {

Logger.getLogger(TimDuongDi.class.getName()).log(Level.SEVERE, null, ex);

}

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

for (int j = 0; j < 10; j++) {

matran[i][j] = str[i * 10 + j];

Trang 14

//System.out.print(matran[i][j]);

}

//System.out.println();

}

this.chuanbi();

}

//Khoi tao gia tri ban dau

private void chuanbi() {

for (i = 0; i < m; ++i) {

for (j = 0; j < n; ++j) {

c[i][j] = 0;

chuaxet[i][j] = 1;

}

}

c[0][0] = 0;

d = 0;

max = 200;

}

//Kiem tra diem tiep theo co the di tiep hay ko

private int ok(int i, int j) {

//System.out.println(i + " " + j);

if ((i < 0) || (i >= m) || (j < 0) || (j >= n)) {

return 0;

} else {

return 1;

}

}

//Tim duong di cho ma tran

private void tim(int c[][]) {

s = 0;

x[0] = outx;//diem den theo toa do x

y[0] = outy;//diem den theo toa do y

i = outx;

j = outy;

int k;

//Tim toa do x,y cho nhung diem con lai dua vao chi so cua ma tran c[][] while (c[i][j] != 0) {

k = c[i][j];//Xac dinh xem buoc truoc minh la phai di theo huong nao

Trang 15

j = j - dy[k];//lay lai toa do y cua buoc truoc

s++;

x[s] = i;

y[s] = j;

}

//for(int element:x)

//System.out.println(x[0]);

}

// Duyen ma tran de tim duong di ngan nhat

private void search(int i, int j, int dem, int[][] chuaxet) {

int[][] cx = new int[50][50];//tao lai mang chuaxet cho moi buoc di for (int g = 0; g < 10; g++) {

for (int h = 0; h < 10; h++) {

cx[g][h] = chuaxet[g][h];

}

}

//kiem tra xem co phai diem tiep theo la diem den

if ((i == outx) && (j == outy)) {

if (max > dem) {

max = dem;

d = 1;

try {

tim(c);

} catch (Exception e) {

System.out.print(e);

}

}

} else {

cx[inx][iny] = 0;//lam cho diem dau vao da xet roi

dem++;

for (int k = 1; k <= 4; ++k) {

int a = i + dx[k];

int b = j + dy[k];

//Kiem tra xem diem tiep theo co di tiep duoc hay ko

if ((ok(a, b)) == 1 && (cx[a][b]) == 1) {

try {

if (matran[a][b] == 0) {

c[a][b] = k;

Ngày đăng: 05/01/2016, 09:38

TỪ KHÓA LIÊN QUAN

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

TÀI LIỆU LIÊN QUAN

🧩 Sản phẩm bạn có thể quan tâm

w