Cho một tập hợp các ngăn xếp NXP với N là số lượng ngăn xếp và P là dung lượng ngăn xếp, làm thế nào tôi có thể tính được số lượng hoán đổi tối thiểu cần thiết để di chuyển từ một nút ở vị trí A đến một vị trí B tùy ý? Tôi đang thiết kế một trò chơi, và mục tiêu cuối cùng là sắp xếp tất cả các ngăn xếp sao cho chúng có cùng màu.
# Let "-" represent blank spaces, and assume the stacks are
stacks = [
['R', 'R', 'R', 'R'],
['Y', 'Y', 'Y', 'Y'],
['G', 'G', 'G', 'G'],
['-', '-', '-', 'B'],
['-', 'B', 'B', 'B']
]
Nếu tôi muốn chèn "B" stacks[1][1]
như vậy stacks[1] = ["-", "B", "Y", "Y"]
. Làm thế nào tôi có thể xác định số lượng di chuyển tối thiểu cần thiết để làm như vậy?
Tôi đã xem xét nhiều cách tiếp cận, tôi đã thử các thuật toán di truyền tạo ra tất cả các động thái có thể từ một trạng thái, ghi điểm và sau đó tiếp tục các con đường ghi điểm tốt nhất, tôi cũng đã cố gắng chạy thuật toán của Djikstra để tìm ra vấn đề . Nó có vẻ đơn giản một cách khó chịu, nhưng tôi không thể tìm ra cách để nó chạy trong bất cứ thứ gì ngoài thời gian theo cấp số nhân. Có một thuật toán tôi đang thiếu có thể áp dụng ở đây không?
Biên tập
Tôi đã viết hàm này để tính số lần di chuyển tối thiểu cần thiết: ngăn xếp: Danh sách danh sách các ký tự đại diện cho các mảnh trong ngăn xếp, ngăn xếp [0] [0] là đỉnh của ngăn xếp [0] stack_ind: Chỉ mục của ngăn xếp mà mảnh sẽ được thêm vào nhu cầu: mảnh cần được thêm vào ngăn xếp cần_index: Chỉ mục nơi mảnh được đặt
def calculate_min_moves(stacks, stack_ind, needs_piece, needs_index):
# Minimum moves needed to empty the stack that will receive the piece so that it can hold the piece
num_removals = 0
for s in stacks[stack_ind][:needs_index+1]:
if item != "-":
num_removals += 1
min_to_unlock = 1000
unlock_from = -1
for i, stack in enumerate(stacks):
if i != stack_ind:
for k, piece in enumerate(stack):
if piece == needs_piece:
if k < min_to_unlock:
min_to_unlock = k
unlock_from = i
num_free_spaces = 0
free_space_map = {}
for i, stack in enumerate(stacks):
if i != stack_ind and i != unlock_from:
c = stack.count("-")
num_free_spaces += c
free_space_map[i] = c
if num_removals + min_to_unlock <= num_free_spaces:
print("No shuffling needed, there's enough free space to move all the extra nodes out of the way")
else:
# HERE
print("case 2, things need shuffled")
Chỉnh sửa: Các trường hợp thử nghiệm trên ngăn xếp:
stacks = [
['R', 'R', 'R', 'R'],
['Y', 'Y', 'Y', 'Y'],
['G', 'G', 'G', 'G'],
['-', '-', '-', 'B'],
['-', 'B', 'B', 'B']
]
Case 1: stacks[4][1] should be 'G'
Move 'B' from stacks[4][1] to stacks[3][2]
Move 'G' from stacks[2][0] to stacks[4][1]
num_removals = 0 # 'G' is directly accessible as the top of stack 2
min_to_unlock = 1 # stack 4 has 1 piece that needs removed
free_spaces = 3 # stack 3 has free spaces and no pieces need moved to or from it
moves = [[4, 3], [2, 4]]
min_moves = 2
# This is easy to calculate
Case 2: stacks[0][3] should be 'B'
Move 'B' from stacks[3][3] to stack[4][0]
Move 'R' from stacks[0][0] to stacks[3][3]
Move 'R' from stacks[0][1] to stacks[3][2]
Move 'R' from stacks[0][2] to stacks[3][1]
Move 'R' from stacks[0][3] to stacks[3][0]
Move 'B' from stacks[4][0] to stacks[0][3]
num_removals = 0 # 'B' is directly accessible
min_to_unlock = 4 # stack 0 has 4 pieces that need removed
free_spaces = 3 # If stack 3 and 4 were switched this would be 1
moves = [[3, 4], [0, 3], [0, 3], [0, 3], [0, 3], [4, 0]]
min_moves = 6
#This is hard to calculate
Việc triển khai mã thực tế không phải là phần khó khăn, nó quyết định cách triển khai thuật toán giải quyết vấn đề mà tôi đang phải vật lộn.
Theo yêu cầu của @ YonIif, tôi đã tạo ra một ý chính cho vấn đề.
Khi nó chạy, nó tạo ra một mảng ngẫu nhiên của các ngăn xếp và chọn một mảnh ngẫu nhiên cần được chèn vào một ngăn xếp ngẫu nhiên tại một vị trí ngẫu nhiên.
Chạy nó in một cái gì đó định dạng này đến bàn điều khiển.
All Stacks: [['-', '-', 'O', 'Y'], ['-', 'P', 'P', 'O'], ['-', 'P', 'O', 'Y'], ['Y', 'Y', 'O', 'P']]
Stack 0 is currently ['-', '-', 'O', 'Y']
Stack 0 should be ['-', '-', '-', 'P']
Cập nhật trạng thái
Tôi rất quyết tâm để giải quyết vấn đề này bằng cách nào đó .
Hãy nhớ rằng có nhiều cách để giảm thiểu số lượng các trường hợp, chẳng hạn như những trường hợp @Hans Olsson được đề cập trong các bình luận. Cách tiếp cận gần đây nhất của tôi đối với vấn đề này, là phát triển một bộ quy tắc tương tự như các quy tắc được đề cập và sử dụng chúng trong một thuật toán thế hệ.
Các quy tắc như:
Đừng bao giờ đảo ngược một động thái. Đi từ 1-> 0 rồi 0-> 1 (Không có ý nghĩa)
Đừng bao giờ di chuyển một mảnh hai lần liên tiếp. Không bao giờ di chuyển từ 0 -> 1 rồi 1 -> 3
Đưa ra một số di chuyển từ ngăn xếp [X] sang ngăn xếp [Y], sau đó một số lần di chuyển, sau đó chuyển từ ngăn xếp [Y] sang ngăn xếp [Z], nếu ngăn xếp [Z] ở trạng thái giống như khi di chuyển từ ngăn xếp [X] đến ngăn xếp [Y] đã xảy ra, một động thái có thể đã được loại bỏ bằng cách chuyển trực tiếp từ ngăn xếp [X] sang ngăn xếp [Z]
Hiện tại, tôi đang tiếp cận vấn đề này với nỗ lực tạo ra đủ quy tắc, rằng nó giảm thiểu số lần di chuyển "hợp lệ", đủ để có thể tính toán câu trả lời bằng thuật toán thế hệ. Nếu bất cứ ai có thể nghĩ về các quy tắc bổ sung, tôi sẽ muốn nghe họ trong các ý kiến.
Cập nhật
Nhờ câu trả lời của @RootTwo tôi đã có một chút đột phá, mà tôi sẽ phác thảo ở đây.
Lên bước đột phá
Xác định chiều cao mục tiêu vì độ sâu của mảnh mục tiêu phải được đặt trong ngăn xếp đích.
Bất cứ khi nào một số mục tiêu được đặt ở chỉ mục <= stack_height - chiều cao mục tiêu, sẽ luôn có một con đường ngắn nhất để chiến thắng thông qua phương thức clear_path ().
Let S represent some solid Piece.
I E
Stacks = [ [R, R, G], [G, G, R], [-, -, -] ]
Goal = Stacks[0][2] = R
Goal Height = 2.
Stack Height - Goal Height = 0
Đưa ra một số ngăn xếp như vậy stack[0] = R
, trò chơi được chiến thắng.
GOAL
[ [ (S | -), (S | -), (S | -) ], [R, S, S], [(S | - ), (S | -), (S | -)] ]
Vì được biết rằng chúng luôn có ít nhất stack_height khoảng trống có sẵn, trường hợp xấu nhất có thể là:
[ [ S, S, !Goal ], [R, S, S], [-, -, -]
Vì chúng tôi biết mảnh mục tiêu không thể ở đích đến hoặc trò chơi được thắng. Trong trường hợp đó, số lần di chuyển tối thiểu cần thiết sẽ là các lần di chuyển:
(0, 2), (0, 2), (0, 2), (1, 0)
Stacks = [ [R, G, G], [-, R, R], [-, -, G] ]
Goal = Stack[0][1] = R
Stack Height - Goal Height = 1
Đưa ra một số ngăn xếp như vậy stack[1] = R
, trò chơi được chiến thắng.
GOAL
[ [ (S | -), (S | -), S], [ (S | -), R, S], [(S | -), (S | -), (S | -)]
Chúng tôi biết có ít nhất 3 khoảng trống có sẵn, vì vậy trường hợp xấu nhất có thể xảy ra là:
[ [ S, !Goal, S], [S, R, S], [ -, -, - ]
Trong trường hợp này, số lần di chuyển tối thiểu sẽ là số lần di chuyển:
(1, 2), (0, 2), (0, 2), (1, 0)
Điều này sẽ giữ cho tất cả các trường hợp.
Do đó, vấn đề đã được giảm xuống thành vấn đề tìm số lượng di chuyển tối thiểu cần thiết để đặt mảnh mục tiêu ở hoặc cao hơn ở độ cao mục tiêu.
Điều này phân chia vấn đề thành một loạt các vấn đề phụ:
Khi ngăn xếp đích có phần có thể truy cập được! = Mục tiêu, xác định xem có vị trí hợp lệ cho phần đó hay không, nếu phần đó sẽ ở đó trong khi phần khác được hoán đổi.
Khi ngăn xếp đích có phần có thể truy cập == phần mục tiêu, xác định xem nó có thể được gỡ bỏ và đặt ở độ cao mục tiêu yêu cầu hay không, nếu phần đó nên ở lại trong khi phần khác được hoán đổi.
Khi hai trường hợp trên yêu cầu hoán đổi một mảnh khác, xác định mảnh nào sẽ hoán đổi để tăng lên để mảnh mục tiêu có thể đạt được chiều cao mục tiêu.
Ngăn xếp đích phải luôn luôn được đánh giá các trường hợp đầu tiên.
I E
stacks = [ [-, R, G], [-, R, G], [-, R, G] ]
Goal = stacks[0][1] = G
Kiểm tra Ngăn xếp Mục tiêu trước tiên dẫn đến:
(0, 1), (0, 2), (1, 0), (2, 0) = 4 Moves
Bỏ qua ngăn xếp mục tiêu:
(1, 0), (1, 2), (0, 1), (0, 1), (2, 0) = 5 Moves