Tải bản đầy đủ (.doc) (41 trang)

ĐỒ ÁN GIẢI THUẬT LẬP TRÌNH

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (862.92 KB, 41 trang )

ĐỒ ÁN
GIẢI THUẬT & LẬP TRÌNH
Đề tài 39: Bài toán Mã Đi Tuần
Knight’s Tour


Đồ án Giải thuật & Lập trình

LỜI MỞ ĐẦU
Ngày nay, Tin học ngày càng phát triển nhanh chóng và được ứng
dụng rộng rãi trong mọi lĩnh vực của đời sống xã hội, việc học và nắm bắt
công nghệ mới đặc biệt là công nghệ thông tin ngày càng trở nên bức thiết.
Đối với sinh viên trong ngành càng trở nên bức thiết. Đối với sinh viên trong
ngành càng phải tích cực học tập, nắm vững mọi kiến thức về công nghệ
thông tin, trong đó cấu trúc dữ liệu và giải thuật được xem là cơ sở, nền tảng
đầu tiên.
Cấu trúc dữ liệu giúp cho sinh viên hiểu được tầm quan trọng của giải
thuật và cách tổ chức cấu trúc dữ liệu để giải quyết bài toán cụ thể.
Sau một thời gian học tập và nghiên cứu ngôn ngữ lập trình C (C
Programming Language), môn Cấu trúc dữ liệu, Phân tích thiết kế giải
thuật, để nắm bắt những kiến thức đã học một cách tốt hơn, nhóm bọn em đã
thực hiện đề tài: “Mã Đi Tuần” (Knight’s Tour).
Trong quá trình thực hiện đề tài, mặc dù đã chăm chỉ nghiên cứu và tìm
hiểu,song chắc chắn khó tránh khỏi những thiếu sót,vì vậy, chúng em rất
mong nhận được những sự chỉ dẫn, ý kiến đóng góp của quý thầy cô để chúng
em ngày càng hoàn thiện hơn. Đồng thời chúng em xin gửi lời cảm ơn sâu sắc
và chân thành đến cô Lê Thị Mỹ Hạnh đã giúp chúng em hoàn thành đề tài
này

Nhóm sinh viên thực hiện


Trang


Đồ án Giải thuật & Lập trình

MỤC LỤC
LỜI MỞ ĐẦU..................................................................................................2
MỤC LỤC.......................................................................................................3
DANH MỤC HÌNH VẼ...................................................................................5
Hình 1. Vị trí quân cờ khi bắt đầu................................................................5
1. GIỚI THIỆU ĐỀ TÀI...................................................................................6
1.1 Tên đề tài....................................................................................................6
2. CƠ SỞ LÝ THUYẾT....................................................................................7
2.1. Ý tưởng..................................................................................................7
2.2. Cơ sở lý thuyết.......................................................................................8
3. TỔ CHỨC CẤU TRÚC DỮ LIỆU VÀ THUẬT TOÁN...............................9
3.1. Phát biểu bài toán...................................................................................9
Xác định đầu vào (Input):............................................................................9
3.2. Cấu trúc dữ liệu......................................................................................9
3.3. Thuật toán............................................................................................12
4. CHƯƠNG TRÌNH VÀ KẾT QUẢ.............................................................14
4.1. Tổ chức chương trình...........................................................................14
4.2. Ngôn ngữ cài đặt..................................................................................22
4.3. Thực hiện chương trình........................................................................23
5. KẾT LUẬN VÀ HƯỚNG PHÁT TRIỂN...................................................40
5.1. Kết luận................................................................................................40
5.2. Hướng phát triển:.................................................................................40
TÀI LIỆU THAM KHẢO..............................................................................41
[1] Giáo trình toán rời rạc – Phan Thanh Tao – Đại học Bách Khoa – Đại học
Đà Nẵng................................................................................................................... 41

[2] />[3] Giải thuật và lập trình – Lê Minh Hoàng..................................................41
[4] Phân tích thiết kế giải thuật – Phạm Nguyên Khang, Đỗ Thanh Nghị .....41

Trang


Đồ án Giải thuật & Lập trình

Trang


Đồ án Giải thuật & Lập trình

DANH MỤC HÌNH VẼ
Hình 1. Vị trí quân cờ khi bắt đầu
Hình 2. Cách đi của con mã
Hình 3. Thay đổi của con mã khi di chuyển
Hình 4. Mô tả các đỉnh trên bàn cờ 4*4

Trang


Đồ án Giải thuật & Lập trình

1. GIỚI THIỆU ĐỀ TÀI
1.1 Tên đề tài
ĐỀ TÀI 39: Bài toán Mã Đi Tuần: Con mã xuất phát từ một vị trí bất
kỳ trên bàn cờ vua, đi qua tất cả các vị trí trên bàn cờ, cuối cùng trở về vị trí
xuất phát
Bài toán “ MÃ ĐI TUẦN” ( Knight’s Tour) là một bài toán liên quan tới

một quân mã trên bàn cờ vua. Quân mã được đặt tại một vị trí trống bất kỳ
trên bàn cờ và di chuyển theo luật cờ vua. Nó nhảy qua mỗi ô trên bàn cờ
chính xác một lần. Hành trình của con mã được gọi là “Hành trình đóng”
(Closed Tour) nếu vị trí kết thúc của con mã có thể đi được về vị trí xuất phát
của nó ( để quân mã có thể đi tiếp một vòng nữa liền ngay sau đó). Nếu không
thì được gọi là “ Hành trình mở”.
1.2 Lý do lựa chọn đề tài
Bài toán mã đi tuần là một bài toán thú vị,
tương đối kinh điển, có nhiều hướng giải quyết, vì
vậy nhóm em đã chọn đề tài “ MÃ ĐI TUẦN ” này
làm đề tài cho môn học của mình.
1.3. Mục đích của đề
Đề tài “ Mã đi tuần ” nhằm mục đích: Giúp bản thân chúng em có thể
hiểu rõ hơn, sâu hơn về các giải thuật và lựa chọn cấu trúc dữ liệu để giải
quyết một bài toán Tin học, từ đó nâng cao khả năng tự học, rèn luyện kỹ
năng lập trình của bản thân.

Trang


Đồ án Giải thuật & Lập trình

2. CƠ SỞ LÝ THUYẾT
2.1. Ý tưởng

Giới thiệu bàn cờ: Theo Wikipedia có lẽ cờ vua xuất hiện vào khoảng
thế kỉ thứ 6 và phát triển trở thành môn trò chơi được nhiều người biết đến
như hiện nay.Trò chơi diễn ra trên bàn cờ hình vuông có 8 hàng (đánh số từ 1
tới 8) và 8 cột (đánh chữ từ a tới h) tạo thành một bàn cờ có 64 ô vuông với 2
màu đen trắng xen kẽ. Mỗi người bắt đầu với 16 quân cờ gồm 1 vua, 1 hậu, 2

mã, 2 tượng, 2 xe và 8 tốt được sắp xếp theo quy luật của bàn cờ. Vị trí a và h
là vị trí của 2 con xe, b và g là vị trí 2 con mã, c và f là vị trí 2 con tượng, d là
vị trí con hậu, e là vị trí con vua, còn hàng liền trên là vị trí của 8 con tốt
(hình 1)

Hình 1. Vị trí quân cờ khi bắt đầu
Trong bàn cờ vua, con mã là quân cờ có nước đi phức tạp nhất. Mã đi
theo hình chữ L. Tiến hoặc lùi 2 ô sau đó sang trái hoặc phải 1 ô. Qua trái
hoặc phải 2 ô sau đó tiến hoặc lùi 1 ô. Mã có thể di chuyển tới một trong các ô
có vòng tròn đỏ như hình 2.

Trang


Đồ án Giải thuật & Lập trình

Hình 2. Cách đi của con mã
Ý tưởng thực hiện: Ta cho quân mã xuất phát tại một ô bất kỳ trên bàn
cờ, cho quân mã đi đến các ô mà quân mã chưa hề đi qua, nếu tại một vị trí đi
nào đó không tìm được nước đi thì lùi quân mã về vị trí trước đó và tiếp tục đi
hướng khác, cho đến khi quân mã thỏa mãn điều kiện bài toán.
2.2. Cơ sở lý thuyết

Bài toán “ Mã đi tuần ” là một dạng của bài toán tổng quát hơn là bài
toán toán tìm đường đi Hamiton trong Lý thuyết đồ thị [1]. Bài toán tìm hành
trình đóng của quân mã là bài toán cụ thể của bài toán tìm chu trình
Hamiltonian.
Đồ thị: Là một cấu trúc rời rạc gồm các đỉnh và cạnh nối các đỉnh đó.
Đường đi Hamilton: Đường đi qua tất cả các đỉnh của đồ thị, mỗi đỉnh duy
nhất một lần.

Chu trình Hamilton: Chu trình đi qua tất cả các đỉnh của đồ thị, mỗi đỉnh
duy nhất một lần.

Trang


Đồ án Giải thuật & Lập trình

Để hiểu rõ hơn về Lý thuyết đồ thị và chu trình Hamilton có thể tìm
đọc cuốn sách “ Toán rời rạc ” ở mục tài liệu tham khảo.
Có nhiều thuật toán có thể áp dụng để tìm kiềm đường đi Hamilton như
quy hoạch động, thuật toán ngẫu nhiên, Heuristic, quay lui,…..
Tuy nhiên, trong báo cáo này, chúng em sẽ áp dụng thuật toán quay lui
để tìm lời giải cho bài toán “ Mã đi tuần”.
3. TỔ CHỨC CẤU TRÚC DỮ LIỆU VÀ THUẬT TOÁN
3.1. Phát biểu bài toán

Xác định đầu vào (Input):
- Một bàn cờ có kích thước n*n (với n là số nguyên dương)
- Tọa độ xuất phát của quân mã
Xác định đầu ra (Output):
Một bảng vuông (bàn cờ) có đánh dấu vị trí nước đi theo thứ tự từ 1
(vị trí xuất phát) đến n*n (vị trí kết thúc) của quân mã.
3.2. Cấu trúc dữ liệu

Trong bài này sẽ sử dụng hai cấu trúc dữ liệu mảng là mảng một chiều
b[n] và mảng hai chiều a[n][n].
+ Cấu trúc dữ liệu mảng một chiều b[n]:
Ta coi bàn cờ n*n như một đồ thị vô hướng và thừa nhận ô (i,j) của bàn
cờ là đỉnh (i-1) *n+j của đồ thị. Với i,j chạy từ 1-> 8.

Ví dụ ô (1,n) là đỉnh thứ (1-1)*n +n = n của đồ thị. Như vậy việc tìm
hành trình con mã để đi hết các ô của bàn cờ <=> tìm ra một hành trình đi hết
các đỉnh của đồ thị, mỗi đỉnh quả một lần.
+ Việc kiểm tra từ đỉnh u tới đỉnh v hay không ta có thể thấy như sau :

Trang


Đồ án Giải thuật & Lập trình

Giả sử tọa độ (x1,y1) có đỉnh ở đồ thị là u -> (x1-1)*n + y1 = u.
Dễ thấy nếu (u % n =0) thì x1= u /n, y1=n, còn nếu (u % n != 0) thì
x1= u/n +1, y1= u%n.
Giả sử tọa độ (x2, y2) có đỉnh ở đồ thị là v -> (x2-1)*n + y2 = v.
Dễ thấy nếu (v % n = 0) thì x2 = v /n, y2 = n , còn nếu (v % n != 0) thì
x2= v/n + 1, y2= v%n.
Ví dụ theo cách tính ở trên tọa độ (2,2) sẽ có đỉnh là 6. Có thể theo dõi
Hình 4
y(1,4)

1

2

3

4

5


6

7

8

9

10

11

12

13

14

15

16

x(1,4)
Hình 3. Mô tả các đỉnh trên bàn cờ 4*4
Từ đó ta có thể kiểm tra từ u có đến v được hay không dựa vào việc so sánh
giá trị |x1-x2| * |y1-y2| với giá trị 2,nếu giá trị đó bằng 2 thì quân mã có thể di
chuyển từ u → v,và ngược lại.

Trang 10



Đồ án Giải thuật & Lập trình

Sử dụng mảng b[n] để xác định trạng thái ô thứ i ( i từ 1 -> n*n ).
- Quy ước :
-

b[i] = 0 : ô thứ i chưa được đi qua.

-

b[i] = k : ô thứ i đã được đi qua và là bước đi thứ k của

quân mã (1<= k <= 2^n)

+ Cấu trúc dữ liệu mảng hai chiều a[n][n]
Xem mỗi ô trên bàn cờ có tọa độ (x,y) với x, y (1->n), ta sử dụng một mảng 2
chiều a[n][n] để biểu diễn tọa độ các ô.
- Quy ước:
-a[x][y]=0: ô (x, y) là vị trí con mã chưa đi qua
-a[x][y]=k: là vị trí con mã đã đi qua bước thứ k (1<= k <= 2^n)
-a[x][y]=1: ô (x, y) là vị trí con mã bắt đầu xuất phát
-Dùng mảng dx[8], cy[8] để lưu trữ sai biệt về tọa độ khi quân mã dy
chuyển qua các ô trên bàn cờ.
-Tại vị trí bất kỳ của con mã trên bàn cờ:
• Khi con mã lùi 2 dòng, lùi 1 cột thì tương ứng dx[]= -2, cy[]= -1
• Khi con mã lùi 1 dòng, lùi 2 cột thì tương ứng dx[]= -1, cy[]= -2
•Tương tự với các vị trí khác trong bàn cờ
=> Ta thu được 2 mảng như sau:
int dx[]={-1,-2,-2,-1,1,2,2,1};

int cy[]={2,1,-1,-2,-2,-1,1,2};

Trang 11


Đồ án Giải thuật & Lập trình

y ( 1 -> n)
x ( 1 -> n)

Hình 4. Thay đổi khi con mã di chuyển
3.3. Thuật toán

Thuật toán đệ quy quay lui:
Tư tưởng chính của thuật toán: Tại mỗi bước cho quân mã thử tất cả
các bước đi kế tiếp. Với mỗi bước đi, kiểm tra xem nếu nước đi hợp lệ
(chưa đi qua và ở trong bàn cờ) thì thử đi nước này. Nếu quân mã đi qua
hết bàn cờ thì xuất kết quả. Ngược lại thì gọi đệ quy tiếp cho vị trí mới thử
trên. Mỗi vị trí đã đi qua được đánh dấu bằng chính thứ tự nước đi trên bàn
cờ. Sau khi không thử vị trí này thì phải bỏ đánh dấu để chọn giải pháp
khác (trường hợp quay lui).

Trang 12


Đồ án Giải thuật & Lập trình

+ Mã giả áp dụng thuật toán quay lui trên cấu trúc dữ liệu mảng một
chiều b[n].
Void madituan()

{
For( v=1;v<=n;v++)
{
If(thỏa mãn là nước đi kế)
Ghi nhận nước đi;
If( là nước đi cuối và có thể về vị trí xuất phát)
Ghi nhận kết quả;
Else
Madituan();
Xóa nước đi;
}
}
+ Mã giả áp dụng thuật toán quay lui trên cấu trúc dữ liệu mảng hai
chiều a[][]
Void madituan()
{
for( int i=1;i<=8;i++)
{
If( chưa đi qua và trong bàn cờ)

Trang 13


Đồ án Giải thuật & Lập trình

{
Đánh dấu nước đi;
If ( là nước đi cuối và có thể trở về vtri xuất phát)
Ghi nhan kq;
Else

{
Madituan();
Xoavtri;
}
}
}
}
4. CHƯƠNG TRÌNH VÀ KẾT QUẢ
4.1. Tổ chức chương trình

Source code:
+ Chương trình sử dụng cấu trúc dữ liệu mảng một chiều và phương
pháp đệ quy quay lui.
#include<stdio.h>
#include<conio.h>
#include<math.h>
int b[300];
int X;

Trang 14


Đồ án Giải thuật & Lập trình

int n,x,y,count=1,dem=0;

void docfile(){
FILE *f;
f=fopen("input.txt","r");
fscanf(f,"%d",&n);

fscanf(f,"%d",&x);
fscanf(f,"%d",&y);
}
void ghifile(){
FILE *f;
f=fopen("output.txt","w+");
fprintf(f,"Ket qua \n");
int u;
for(u=1;u<=n*n;u++)
{
if(u%n==0) fprintf(f,"%5d\n\n",b[u]);
else fprintf(f,"%5d",b[u]);
}
fprintf(f,"\n");
}

Trang 15


Đồ án Giải thuật & Lập trình

int kt(int u,int v)
{
int x1,x2,y1,y2,tmp=0;
x1=((u-1)/n) +1;
if(u%n==0) y1=n;
else y1=u%n;
x2=((v-1)/n) +1;
if(v%n==0) y2=n;
else y2=v%n;

if(fabs(x1-x2)*fabs(y1-y2)==2) tmp=1;
return tmp;
}
void in()
{
for(int u=1;u<=n*n;u++)
{
if(u%n==0) printf("%5d\n\n",b[u]);
else printf("%5d",b[u]);
}
printf("----------------------------------\n");
}

Trang 16


Đồ án Giải thuật & Lập trình

void madituan(int u)
{
int v,tmp;
for(v=1;v<=n*n;v++)
{
if(dem==1) break;
if((b[v]==0)&&(kt(u,v)==1))
{
count++;
b[v]=count;
if(count==n*n&&(kt(X,v)==1))
{

printf("\n");
dem++;
in();
ghifile();
goto next;
}
else
{
next :madituan(v);

Trang 17


Đồ án Giải thuật & Lập trình

count--;
b[v]=0;
}
}
}
}
int main()
{
for(int i=1;i<=n*n;i++)
{
b[i]=0;
}
docfile();
X=(x-1)*n+y;
b[X]=1;

madituan(X);
return 0;
getch();
}

Trang 18


Đồ án Giải thuật & Lập trình

+Chương trình sử dụng cấu trúc mảng hai chiều và phương pháp đệ quy
quay lui:
#include<stdio.h>
#include<conio.h>
int dx[]={-1,-2,-2,-1,1,2,2,1};
int cy[]={2,1,-1,-2,-2,-1,1,2};
int dem=0;
int n;
int a[300][300];
int d,c;
void docfile(){
FILE *f;
f=fopen("input.txt","r");
fscanf(f,"%d",&n);
fscanf(f,"%d",&d);
fscanf(f,"%d",&c);
}
void ghifile2(){
FILE *f;
f=fopen("output.txt","w+");

fprintf(f,"Ket qua \n");
for(int i=0;i
Trang 19


Đồ án Giải thuật & Lập trình

for(int j=0;jfprintf(f,"%5d",a[i][j]);
fprintf(f,"\n\n");
}
fprintf(f,"\n");
}
void khoitao(){
for(int i=0;ifor(int j=0;ja[i][j]=0;
}
void in(){
for(int i=0;ifor(int j=0;jprintf("%5d",a[i][j]);
printf("\n\n");
}
printf("\n");
}
int check(int d,int c){
int t=0;
for(int i=0;i<8;i++){


Trang 20


Đồ án Giải thuật & Lập trình

if(a[d+dx[i]][c+cy[i]]==1&&d+dx[i]>=0&&d+dx[i]&&c+cy[i]>=0&&c+cy[i]t=1;
}return t;
}
void madituan(int x,int y,int k){
int u,v;
for (int i=0;i<8;i++)
{
if(dem==1) break; //gioi han chi hien thi 1 ket qua.
u=x+dx[i];
v=y+cy[i];
if(a[u][v]==0
&&u>=0&&u&&v>=0&&v{
a[u][v]=k;
if(k==n*n&&check(u,v)==1){dem++;in();ghifile2();goto next;
}
else
{
next :madituan(u,v,k+1);

Trang 21



Đồ án Giải thuật & Lập trình

a[u][v]=0;
}
}
}
}
int main(){
khoitao();
docfile();
d=d-1;
c=c-1;
a[d][c]=1;
madituan(d,c,2);
return 0;
getch();
}
4.2. Ngôn ngữ cài đặt

Ngôn ngữ C

Trang 22


Đồ án Giải thuật & Lập trình
4.3. Thực hiện chương trình

4.3.1 Kết quả sau khi chạy chương trình

Sau khi biên dịch và chạy chương trình chúng em có bảng sau:
Kết quả thời gian (giây) thực hiện chương trình với bàn cờ kích thước 6*6

CTDL
Mảng một chiều – pp quay lui

Mảng hai chiều – pp quay lui

Tọa độ
(1,1)

0.1498

0.1004

(1,3)

0.1971

x

(2,3)

0.1418

0.1327

(2,5)

0.2228


x

(4,2)

0.1436

0.2818

(6,6)

x

0.1237

(với x là thời gian đợi thực hiện chương trình quá lâu)
Dựa vào bảng kết quả trên ta có nhận xét:
Sau khi cài đặt và chạy chương trình chúng em nhận thấy chương trình
trả về kết quả đúng thỏa mãn hành trình đóng của quân mã với thời gian ngắn
(<0.2 giây), nhưng khi cho kích thước của bàn cờ lớn từ 8*8 trở lên thời gian
thực hiện chương trình rất lâu, có thể xem như không tìm được hành trình. Để
giải quyết vấn đề này chúng em đã tìm và nghiên cứu phương pháp duyệt ưu
tiên Warndorff như một cách để giảm thời gian thực hiện chương trình.
Phương pháp duyệt ưu tiên Warndorff : nếu gọi k là số ô kề với ô x và
chưa đi qua (kề ở đây theo nghĩa đĩnh kề chứ không phải ô kề cạnh) thì từ một
ô ta sẻ không thử xét lần lượt hướng đi có thể, mà ta sẻ ưu tiên thử hướng đi
tới ô có k nhỏ nhất trước.

Trang 23



Đồ án Giải thuật & Lập trình

4.3.2 Áp dụng phương pháp duyệt ưu tiên Warndorff
+ Mã giả áp dụng duyệt ưu tiên vào chương trình sử dụng cấu trúc
dữ liệu mảng 1 chiều b[] – phương pháp quay lui
Int dembac(int u)
{
For(v từ 1 -> n*n)
{
If(thỏa mã là nước đi kế) tăng biến đếm lên 1;
}
Trả về biến đếm;
}
Int bacnhonhat(int u)
{
Khởi tạo biến temp=10;
For(v từ 1->n*n)
{
If(thỏa mãn là nước đi kế và temp>dembac())
Temp= dembac();
}
Trả về biến temp;
}

Trang 24


Đồ án Giải thuật & Lập trình


Void madituan()
{
For(v=1;v<=n;v++)
{
If(thỏa mãn là nước đi kế và dembac() bằng bacnhonhat())
Ghi nhận nước đi;
If(là nước đi cuối và có thể về vị trí xuất phát)
Ghi nhận kết quả;
Else
Madituan();
Xóa nước đi
}
}
+ Mã giả áp dụng duyệt ưu tiên vào chương trình sử dụng cấu trúc
dữ liệu mảng 2 chiều a[][]- phương pháp quay lui
Sử dụng mảng p[] với mục đích lưu lại số nước đi có thể có của nước
đi tiếp theo từ vị trí hiện tại.
Void toiuu(int d,int c)
{
Khai báo 2 biến dt,ct ;
For(i từ 1->8)
{

Trang 25


×