Bài toán sắp xếp quân Hậu

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

2.2 Thuật toán quay lui (Back Tracking)

2.2.4 Bài toán sắp xếp quân Hậu

Yêu cầu: cho một bàn cờ vua nxn, hãy liệt kê cách sắp xếp n quân hậu sao cho các quân hậu không ăn được nhau! Quân hậu trên bàn cờ có thể ăn quân khác trên cùng hàng, cùng cột hoặc cùng đường chéo.

Phân tích: các quân hậu sẽ được sắp trên các dòng khác nhau do chúng có thể ăn theo hàng ngang. Để dễ phân tích ta mô tả quân hậu theo dòng; quân hậu 1 ở dòng 1, quân hậu i ở dòng i…Do mỗi quân hậu chắc chắn nằm trên các dòng khác nhau nên ta chỉ cần xác định vị trí cột của mỗi quân hậu là xong.

Tiếp theo ta xét những ràng buộc theo đường chéo, có hai đường chéo:

o Chéo “\”: theo hướng Trên Trái - Dưới Phải o Chéo “/”: theo hướng Trên Phải - Dưới Trái

Hình 3.5: Các nước đi của quân hậu có thể có.

Hình 3.6: Một cách sắp xếp 8 hậu trên bàn cờ 8x8

Các đường chéo Trên Trái - Dưới Phải như hình vẽ dưới, mỗi đường chéo này sẽ đi qua các ô, các ô này có tính chất: dòng - cột = C (hằng số). Do đó với mỗi đường chéo ta có 1 hằng số C và 1-n ≤ C ≤ n-1 xác định duy nhất đường chéo đó.

Hình 3.7: Các đường chéo Trên Trái - Dưới Phải

Các đường chéo Trên Phải - Dưới Trái: mỗi đường chéo này sẽ đi qua các ô có tính chất sau: dòng + cột = C (hằng số). Do đó với mỗi đường chéo ta có một hằng số C và 2 ≤ C ≤ 2n.

Hình 3.8: Các đường chéo Trên Phải - Dưới Trái.

1 2 3 4 5 6 7 8

1 2 3 4 5 6 7 8

0 -1 -7

7 4

1 2 3 4 5 6 7 8

1 2 3 4 5 6 7 8

16 14 2 9

Hình 3.9: Vị trí của quân hậu ảnh hưởng đến 2 đường chéo.

Cài đặt:

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

o Mảng R[N]: lưu theo cột, R[i] = true ⇒ cột i còn tự do, ngược lại cột i đã bị quân hậu khống chế.

o Mảng C1[2*N-1]: lưu đường chéo TT-DP, do các dường chéo này có chỉ số từ 1-n ⇒ n-1 nên ánh xạ chỉ số này vào mảng C1 bằng cách cộng thêm (n- 1). Khi đó đường chéo 1-n sẽ có chỉ số là 0 trong mảng C1…

o Mảng C2[2*N+1]: lưu đường chéo TP-DT, các đường chéo này có chỉ số từ 2- 2n nên ta đánh chỉ số C2 từ 2- 2n luôn cho tiện (hai phần tử C2[0] và C2[1] ta không dùng đến).

o Các phần tử của 3 mảng R, C1 và C2 được gán giá trị True khi bắt đầu!

o Thuật toán quay lui :

o Ý tưởng chính như sau: xét tất cả các cột, thử đặt quân hậu 1 vào 1 cột, với mỗi cách đặt quân hậu 1, xét tất cả các đặt quân hậu 2 sao cho quân hậu 1 không ăn được nó, thử đặt quân hậu 2 vào ô có thể…rồi xét tiếp đến quân hậu 3 đến quân hậu n. Với mỗi cách đặt quân hậu n sẽ cho ta một kết quả!

Khi xét hết tất cả các giá trị có thể có gán cho quân hậu thứ i thì thuật toán sẽ quay lên xét những giá trị còn lại của quân hậu thứ i-1.

1 2 3 4 5 6 7 8

1 2 3 4 5 6 7 8

Đừng chéo TT- DP có chỉ số 0

Đừng chéo TP- DT có chỉ số 10

Ô ( 5, 5)

o Khi chọn vị trí j cho quân hậu thứ i, thì ô (i, j) không bị quân hậu đặt trước đó ăn. Do vậy ô (i, j) phải thoả điều kiện:

 Cột j còn tự do.

 Đường chéo TT-DP có chỉ số (i-j) không bị bất kỳ quân hậu nào khống chế.

 Đường chéo TP-DT có chỉ số (i+j) cũng không bị các quân hậu trước đó khống chế.

o Sau khi đặt quân hậu thứ i vào cột j, nếu i = n tức là đặt xong quân hậu cuối cùng ⇒ được một bộ kết quả. Ngược lại

 Đánh dấu 2 đường chéo TT-DP (i-j) và đường TP-DT(i+j) và cột j đã bị khống chế. Tiếp tục gọi đệ quy cho quân thứ i+1.

 Sau khi gọi đệ quy cho quân hậu i+1, ta phải thử vị trí khác cho quân hậu thứ i trong số những giá trị j có thể nhận được. Do đó ta phải bỏ việc đánh dấu cột j và 2 đường chéo, lúc này cột j và 2 đường chéo đó sẽ tự do. Thao tác này cho phép quân hậu khác có thể đặt ở vị trí đó ở những bước tiếp sau.

Chương trình C/C++ minh họa bài toán n-Hậu:

#define MAX 12

void ShowResult(int b[MAX], int n) {

/*Xuat ket qua theo dong*/

for(int i=0; i < n; i++)

printf("(%d, %d)\t", i+1, b[i]+1);

printf("\n");

}

void Try(int *r,int *b, int n, int i, int *c1, int *c2) {

for(int j=0; j < n; j++) //tìm vị trí cột {

if (r[j] && c1[(i-j)+n-1] && c2[i+j]) //kiểm tra cột và 2 chéo {

b[i] = j; // chọn cột j

if (i==n-1)

ShowResult(b,n); // xuất 1 bộ kết quả else

{

r[j] = false; // đánh dấu chọn cột j c1[(i-j)+n-1] = false; //chéo bị hậu khống chế c2[i+j] = false; //chéo bị hậu khống chế Try(r, b, n, i+1, c1, c2); // đặt hậu tiếp theo

r[j] = true; // bỏ chọn cột j

c1[(i-j)+n-1] = true; // chéo tự do c2[i+j] = true; // chéo tự do }

} }

}

int main(int argc, char* argv[]) {

int b[MAX], r[MAX];

int c1[2*MAX-1], c2[2*MAX-1];

int n;

printf("doc n (<12): ");

scanf("%d",&n);

for(int i=0; i < n;i++) r[i] = true;

for(i=0; i < 2*MAX-1; i++) {

c1[i] = c2[i] = true;

}

Try(r, b, n, 0, c1, c2);

return 0;

}

Kết quả khi n = 5

(1, 1) (2, 3) (3, 5) (4, 2) (5, 4) (1, 1) (2, 4) (3, 2) (4, 5) (5, 3) (1, 2) (2, 4) (3, 1) (4, 3) (5, 5) (1, 2) (2, 5) (3, 3) (4, 1) (5, 4) (1, 3) (2, 1) (3, 4) (4, 2) (5, 5) (1, 3) (2, 5) (3, 2) (4, 4) (5, 1) (1, 4) (2, 1) (3, 3) (4, 5) (5, 2) (1, 4) (2, 2) (3, 5) (4, 3) (5, 1)

(1, 5) (2, 2) (3, 4) (4, 1) (5, 3) (1, 5) (2, 3) (3, 1) (4, 4) (5, 2)

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

Tải bản đầy đủ (DOC)

(124 trang)
w