Bài tốn mã đi tuần

Một phần của tài liệu Giáo trình kỹ thuật lập trình 2 (Trang 57 - 63)

Yêu cầu: Cho một bàn cờ tổng quát dạng nxn, hãy chỉ ra một hành trình của một quân Mã, xuất phát từ một vị trí bắt đầu đi qua tất cả các ơ cịn lại của bàn cờ, mỗi ơ đi đúng một lần.

Ý tưởng cơ bản: dùng thuật tốn quay lui; xuất phát từ 1 ơ, gọi số nước đi là t=1, ta cho quân mã thử đi tiếp 1 ơ (cĩ 8 nước đi cĩ thể), nếu ơ đi tiếp này chưa đi qua thì chọn làm bước đi tiếp theo. Tại mỗi nước đi kiểm tra xem tổng số nước đi bằng n*n chưa, nếu bằng thì mã đã đi qua tất cả các ơ ⇒ dừng (do chỉ cần tìm một giải pháp). Trường hợp ngược lại, gọi đệ quy để chọn nước đi tiếp theo. Ngồi ra, nếu tại một bước tìm đường đi, nếu khơng tìm được đường đi tiếp thì thuật tốn sẽ quay lui lại nước đi trước và tìm đường đi khác…

Hình 3.11: Đường đi của quân mã trong bàn cờ 5x5 Cài đặt:

o Cấu trúc dữ liệu:

o Mảng board[MAX][MAX]: lưu bàn cờ, trong đĩ board[i][j] là ơ (i, j); giá trị của board[i][j] là 0 khi quân mã chưa đi qua, và >0 khi quân mã đã đi qua, giá trị board[i][j] lúc này chính là thứ tự nước đi trên hành trình. Thật sự cài đặt mảng board như vậy là đủ, nhưng nếu bổ sung thêm một tí thì sẽ tăng tốc độ thực hiện. Vấn đề bổ sung liên quan đến đường biên, do mỗi lần di chuyển quân mã ta phải kiểm tra xem nước đi cĩ ra ngồi biên hay khơng. Ta cĩ thể mở rộng mảng board để khơng cần phải kiểm tra bằng cách mở rộng hai ơ về bốn hướng trên dưới trái phải. Khi đĩ chỉ cần gán giá trị cho các ơ ngồi biên là -1.

Hình 3.12 : Mảng board cho bàn cờ 8x8 ⇒ 12x12.

o Mảng dr[8], dc[8]: lưu các độ dời của bước đi kế tiếp, cĩ tám nước đi cĩ thể cho vị trí quân mã hiện tại. Do đĩ để đi nước thứ i ta chỉ cần cộng thêm dr[i] cho dịng và dc[i] cho cột!

Hình 3.13: Thứ tự tám nước đi theo chiều kim đồng hồ. Mảng dr[] = {-2, -1, 1, 2, 2, 1, -1, -2}

dc[] = {1, 2, 2, 1, -1, -2, -2, 1} o Thuật giải đệ quy :

Tại mỗi bước lần lượt cho quân mã thử tất cả các nước đi kế tiếp (tám nướ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

(-2, 1) (-1, 2) (1, 2) (2, 1) (2, -1) (1, -2) (-1, -2) (-2, -1)

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. Lưu ý là mỗi khi vị trí đã đi qua được đánh dấu chính 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).

Minh họa hàm Try với step là thứ tự của nước đi, i và j là vị trí của quân mã hiện tại.

Try( int step, int i, j) {

+ Với mỗi nước đi kế tiếp (ii, jj) từ (i, j) + Nếu (ii,jj) hợp lệ

chọn (ii, jj) làm nước đi kế tiếp

+ nếu đi hết bàn cờ

 xuất 1 kết quả

+ ngược lại

Gọi đệ quy Try(step +1, ii, jj)

Khơng chọn (ii, jj) là nước đi kế tiếp

}

Chương trình C/C++ minh họa cho trường hợp bàn cờ 8x8.

#include "stdafx.h" #include "conio.h" #include "stdlib.h"

#define MAX 12 // trường hợp bàn cờ 8x8 void Show(int board[MAX][MAX]);

void Init(int board[MAX][MAX]) {

for(int i=0;i<MAX;i++) for(int j=0;j<MAX;j++)

if(i>=2 && i<=MAX-3 && j>=2 && j<=MAX-3)

board[i][j]=0; // đánh dấu chưa đi qua

else

board[i][j]=-1; // đánh dấu biên } (adsbygoogle = window.adsbygoogle || []).push({});

void Try(int step, int i, int j, int board[MAX][MAX], int *dr, int *dc) {

for(int k=0; k<7; k++) //duyệt qua các nước đi kế tiếp {

if( board[i+dr[k]][j+dc[k]]==0 ) // nếu vị trí này chưa đi qua {

Board[i+dr[k]][j+dc[k]]= step+1; // đánh dấu chọn vị trí if(step+1==64) //hồn tất một kết quả

{

Show(board);

printf("Nhan <ENTER> de tiep tuc tim loi \ giai ke. Nhan <ESC> de thoat"); char c;

if(c = getch() == 27) exit(1);

}

else // gọi đệ quy cho nước kế tiếp

Try(step+1, i+dr[k], j+ dc[k], board, dr, dc);

Board[i+dr[k]][j+dc[k]]= 0;// trả tự do cho vị trí vừa chọn }// end if

}//end for }

void Show(int board[MAX][MAX]) { for(int i=0;i<MAX;i++) { for(int j=0;j<MAX;j++) printf("%4d",board[i][j]); printf("\n\n"); } } void main() { int board[MAX][MAX]; int dr[8]={-2,-1,1, 2, 2, 1,-1,-2}; int dc[8]={1, 2, 2, 1,-1,-2,-2,-1}; Init(board);

board[2][2]=1; // chọn vị trí đầu tiên Show(board);

Try(1, 2, 2, board, dr, dc); }

1 38 43 34 3 36 19 22 44 59 2 37 20 23 4 17 39 42 33 60 35 18 21 10 58 45 40 53 24 11 16 5 41 32 57 46 61 26 9 12 50 47 52 25 54 15 6 27 31 56 49 62 29 8 13 64 48 51 30 55 14 63 28 7 Hình 3.14: Một giải pháp cho bàn cờ 8x8. 

Chương 1

Tìm kiếm và Sắp xếp



Một phần của tài liệu Giáo trình kỹ thuật lập trình 2 (Trang 57 - 63)