In bàn cờ từ dãy vị trí
```python
def showQueen(A, from_bottom=False):
n = len(A)
rows = range(n - 1, -1, -1) if from_bottom else range(n)
for i in rows:
for j in range(n):
print(1 if A[j] == i else 0, end=' ')
print()
```
Vòng ngoài chọn hàng i, vòng trong chọn cột j. Ô đó có quân khi A[j]==i; in 1 cho quân Hậu, 0 cho ô trống. Mặc định hàng 0 được in trên cùng. Tham số from_bottom sẽ được dùng khi đổi chiều đánh số hàng ở bài sau.
Thử tiếp một cột
```python
def trynext(k):
global ncount
if k == n:
ncount += 1
if show_results:
print('Phương án', ncount, ':', A)
showQueen(A, from_bottom)
else:
for i in range(n):
if check(A, i, k):
A[k] = i
trynext(k + 1)
```
check là hàm của bài trước. Trước lời gọi trynext(k), các cột 0…k−1 đã hợp lệ. Mỗi hàng i được thử; nếu check đồng ý thì gán A[k]=i và xử lí cột k+1. Nếu không còn hàng nào phù hợp, lời gọi kết thúc để mức trước thử lựa chọn khác.
Khi k==n, đã đặt đủ quân nên tăng ncount và có thể in nghiệm. show_results chỉ quyết định có in bàn cờ; không đổi bước tìm kiếm. Dãy tạo sẵn được gán đè ở lần thử sau nên không dùng pop. Phần bên phải chưa có hiệu lực luôn được check bỏ qua.
Khởi tạo rồi chạy từ cột 0
Sau khi đã định nghĩa ba hàm, khởi tạo:
```python
n = 4
A = [0] * n
ncount = 0
show_results = True
from_bottom = False
trynext(0)
print('Q(4) =', ncount)
```
Hoặc chạy tệp đầy đủ:
```text
python3 g11-cs-xep-hau.py 4
```
Cần nhận hai dãy [1,3,0,2] và [2,0,3,1], mỗi dãy kèm bàn cờ gồm bốn dòng 0/1; cuối cùng Q(4)=2. Đối chiếu từng cột có một số 1, các hàng và đường chéo không xung đột. Với n=3, nhận Q(3)=0, không có bàn cờ nào được in.
Trong trynext, ncount+=1 gán lại giá trị biến, nên cần global ncount để cập nhật biến tổng thể. Chỉ đọc n hoặc gán A[k] không đòi hỏi gán lại tên n hoặc A. Trước mỗi lần chạy mới phải đặt ncount=0; main trong tệp tải thực hiện việc đó.
Bài luyện
Khi trynext nhận k==n, điều gì đã được hoàn tất?
Nhánh vừa đặt đủ quân là một nghiệm.
Mỗi bước trước đã qua check rồi mới đi tiếp.
Cột đầu ứng với k=0.
Dãy chỉ có các chỉ số 0…n−1.
Lời giải
Các cột 0…n−1 đã được chọn và kiểm hợp lệ. k==n là điểm dừng ghi nhận nghiệm trước khi thử truy cập phần tử ngoài dãy.
Bài luyện
Vì sao chương trình không cần pop sau trynext(k+1)?
Cần giữ đúng ý nghĩa tiền tố ở từng mức.
A có n phần tử.
Không thêm phần tử mới nên không có phần tử cần xóa.
Giá trị phía sau có thể còn từ nhánh trước.
Lời giải
Mô hình dùng dãy tạo sẵn, khác mô hình append–pop. Vị trí đang thử được ghi đè; khi quay về, các cột phía sau bị bỏ qua đến khi được chọn lại. Vì thế không cần xóa cuối dãy.
Bài luyện
Nếu quên global ncount nhưng vẫn dùng ncount+=1 trong hàm, điều gì xảy ra ở nghiệm đầu?
Cần khai báo đúng phạm vi để gán biến tổng thể.
Đây là lỗi phạm vi biến, không phải phép tính ra 0.
Lỗi xuất hiện khi thực hiện phép tăng ở lá.
Phép += phải đọc rồi gán biến cục bộ khi thiếu global.
Lời giải
Có phép gán trong hàm nên nếu thiếu global, ncount được xem là tên cục bộ. += cần đọc giá trị cũ trước khi gán nhưng tên cục bộ chưa được khởi tạo, dẫn tới UnboundLocalError.
Đang học