.......E <-end
........
........
........
........
........
........
S....... <-start
Rất tiếc, bạn không thể sử dụng "bất kỳ thuật toán tìm đường dẫn chuẩn nào" vì đường dẫn của bạn có thể không phải là đường đi ngắn nhất. Bạn sẽ phải sử dụng cụ thể một tìm kiếm ngây thơ mà xem xét tất cả các đường dẫn (chiều sâu đầu tiên hoặc chiều rộng đầu tiên, ví dụ).
Tuy nhiên, vì bạn không quan tâm cách bạn đã vào ô xếp, bạn có thể sử dụng kỹ thuật được gọi là lập trình động. Đối với mỗi vị trí (i, j), các số cách để đạt được điều đó trong n di chuyển (chúng ta hãy gọi nó cách i, j (n)) là:
cách i, j (n) = cách i-1, j (n-1) + cách i + 1, j (n-1) + cách i, j-1 (n-1) + cách i, j + 1 (n-1) + cách i + 1, j + 1 (n-1) + cách i-1, j + 1 (n-1) + cách i + 1, j-1 (n-1) + cách i-1, j-1 (n-1)
Đó là, nhà vua có thể di chuyển từ bất kỳ các ô vuông liền kề trong 1 di chuyển:
cách i, j (n) = tổng hàng xóm (i, j) (cách xóm (n-1))
vì vậy bạn muốn làm, ví dụ trong python:
SIZE = 8
cache = {}
def ways(pos, n):
r,c = pos # row,column
if not (0<=r<SIZE and 0<=c<SIZE):
# off edge of board: no ways to get here
return 0
elif n==0:
# starting position: only one way to get here
return 1 if (r,c)==(0,0) else 0
else:
args = (pos,n)
if not args in cache:
cache[args] = ways((r-1,c), n-1) + ways((r+1,c), n-1) + ways((r,c-1), n-1) + ways((r,c+1), n-1) + ways((r-1,c-1), n-1) + ways((r+1,c-1), n-1) + ways((r+1,c-1), n-1) + ways((r+1,c+1), n-1)
return cache[args]
Demo:
>>> ways((7,7), 15)
1074445298
Các kỹ thuật nói trên được gọi là memoization, và đơn giản hơn để viết hơn các chương trình năng động, bởi vì bạn không cần phải thực sự suy nghĩ về thứ tự mà bạn làm việc.Bạn có thể thấy bộ nhớ cache phát triển như chúng ta thực hiện một loạt các truy vấn lớn hơn và lớn hơn:
>>> cache
{}
>>> ways((1,0), 1)
1
>>> cache
{((1, 0), 1): 1}
>>> ways((1,1), 2)
2
>>> cache
{((0, 1), 1): 1, ((1, 2), 1): 0, ((1, 0), 1): 1, ((0, 0), 1): 0, ((2, 0), 1): 0, ((2, 1), 1): 0, ((1, 1), 2): 2, ((2, 2), 1): 0}
>>> ways((2,1), 3)
5
>>> cache
{((1, 2), 1): 0, ((2, 3), 1): 0, ((2, 0), 2): 1, ((1, 1), 1): 1, ((3, 1), 1): 0, ((4, 0), 1): 0, ((1, 0), 1): 1, ((3, 0), 1): 0, ((0, 0), 1): 0, ((2, 0), 1): 0, ((2, 1), 1): 0, ((4, 1), 1): 0, ((2, 2), 2): 1, ((3, 3), 1): 0, ((0, 1), 1): 1, ((3, 0), 2): 0, ((3, 2), 2): 0, ((3, 2), 1): 0, ((1, 0), 2): 1, ((4, 2), 1): 0, ((4, 3), 1): 0, ((3, 1), 2): 0, ((1, 1), 2): 2, ((2, 2), 1): 0, ((2, 1), 3): 5}
(Trong python, cũng có thể sử dụng một @cached
hoặc @memoized
trang trí để tránh phải viết toàn bộ mã trong else:
khối cuối cùng. Các ngôn ngữ khác có các cách khác để tự động thực hiện ghi nhớ.)
Ở trên là cách tiếp cận từ trên xuống. Đôi khi nó có thể tạo ra các ngăn xếp rất lớn (ngăn xếp của bạn sẽ tăng lên với n
). Nếu bạn muốn siêu hiệu quả để tránh công việc không cần thiết, bạn có thể thực hiện cách tiếp cận từ dưới lên, nơi bạn mô phỏng tất cả các vị trí mà nhà vua có thể, trong 1 bước, 2 bước, 3 bước, ...:
SIZE = 8
def ways(n):
grid = [[0 for row in range(8)] for col in range(8)]
grid[0][0] = 1
def inGrid(r,c):
return all(0<=coord<SIZE for coord in (r,c))
def adjacentSum(pos, grid):
r,c = pos
total = 0
for neighbor in [(1,0),(1,1),(0,1),(-1,1),(-1,0),(-1,-1),(0,-1),(1,-1)]:
delta_r,delta_c = neighbor
(r2,c2) = (r+delta_r,c+delta_c)
if inGrid(r2,c2):
total += grid[r2][c2]
return total
for _ in range(n):
grid = [[adjacentSum((r,c), grid) for r in range(8)] for c in range(8)]
# careful: grid must be replaced atomically, not element-by-element
from pprint import pprint
pprint(grid)
return grid
Demo:
>>> ways(0)
[[1, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0]]
>>> ways(1)
[[0, 1, 0, 0, 0, 0, 0, 0],
[1, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0]]
>>> ways(2)
[[3, 2, 2, 0, 0, 0, 0, 0],
[2, 2, 2, 0, 0, 0, 0, 0],
[2, 2, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0]]
>>> ways(3)
[[6, 11, 6, 4, 0, 0, 0, 0],
[11, 16, 9, 5, 0, 0, 0, 0],
[6, 9, 6, 3, 0, 0, 0, 0],
[4, 5, 3, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0]]
>>> ways(4)
[[38, 48, 45, 20, 9, 0, 0, 0],
[48, 64, 60, 28, 12, 0, 0, 0],
[45, 60, 51, 24, 9, 0, 0, 0],
[20, 28, 24, 12, 4, 0, 0, 0],
[9, 12, 9, 4, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0]]
Một cần phải rất cẩn thận với số nguyên tràn đây. 15 di chuyển sẽ tràn 32 bit và 25 di chuyển sẽ tràn 64 bit. –