给定一组NXP堆栈,其中N为堆栈数,P为堆栈容量,我如何计算从位置A的某个节点移动到任意位置B所需的最小交换数?我正在设计一个游戏,最终目标是对所有堆栈进行排序,以使它们都具有相同的颜色。
# 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']
]
如果我想在插入一个“B” stacks[1][1]
这样stacks[1] = ["-", "B", "Y", "Y"]
。如何确定所需的最小移动次数?
我一直在研究多种方法,尝试过遗传算法,可以从状态生成所有可能的移动,对它们进行评分,然后继续沿最佳得分路径前进,我还尝试运行Djikstra的算法来对问题进行寻路。看起来很简单,但是我无法找到一种方法来让它在指数时间以外的任何时间运行。我是否缺少适用于此的算法?
编辑
我已经编写了此函数来计算所需的最小移动量:堆栈:表示堆栈中棋子的字符列表,堆栈[0] [0]是堆栈[0]的顶部stack_ind:将要添加到堆栈中的片断的堆栈stacks.exe
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")
编辑:堆栈上的测试用例:
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
实际的代码实现并不是困难的部分,它决定了如何实现一种解决我所苦恼的算法的算法。
按照@YonIif的要求,我已经为问题创建了要点。
运行时,它会生成一个堆栈的随机数组,并选择一个需要插入到随机堆栈中随机位置的随机片段。
运行它会将这种格式的内容打印到控制台。
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']
状态更新
我决心以某种方式解决这个问题。
请记住,有一些方法可以减少案件数量,例如评论中提到的@Hans Olsson。对于这个问题,我最近的方法是开发一组与上述规则类似的规则,并将其应用于代代算法中。
规则如下:
永远不要扭转举动。从1-> 0转到0-> 1(毫无意义)
永远不要连续移动两次。从不从0-> 1移至1-> 3
给定从stacks [X]到stacks [Y]的一些移动,然后进行一定数量的移动,然后如果stacks [Z]处于与移动时相同的状态,则从stacks [Y]到stacks [Z]进行移动从stacks [X]到stacks [Y],通过直接从stacks [X]移到stacks [Z]可以消除移动
当前,我正在尝试创建足够的规则来解决这个问题,它可以最大限度地减少“有效”移动的数量,从而可以使用分代算法来计算答案。如果有人可以想到其他规则,那么我很乐意在评论中听到它们。
更新资料
感谢@RootTwo的回答,我取得了一些突破,我将在这里概述。
突破
将目标高度定义为必须将目标块放置在目标堆栈中的深度。
每当将某个目标放置在索引<= stack_height-目标高度时,总是会通过clear_path()方法获得胜利的最短路径。
Let S represent some solid Piece.
IE浏览器
Stacks = [ [R, R, G], [G, G, R], [-, -, -] ]
Goal = Stacks[0][2] = R
Goal Height = 2.
Stack Height - Goal Height = 0
给定一定的筹码stack[0] = R
,游戏就赢了。
GOAL
[ [ (S | -), (S | -), (S | -) ], [R, S, S], [(S | - ), (S | -), (S | -)] ]
因为已知它们总是至少有stack_height个空白空间,所以最坏的情况是:
[ [ S, S, !Goal ], [R, S, S], [-, -, -]
由于我们知道目标片不能在目标位置或赢得比赛。在这种情况下,所需的最少移动次数为:
(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
给定一定的筹码stack[1] = R
,游戏就赢了。
GOAL
[ [ (S | -), (S | -), S], [ (S | -), R, S], [(S | -), (S | -), (S | -)]
我们知道至少有3个空格可用,因此最坏的情况是:
[ [ S, !Goal, S], [S, R, S], [ -, -, - ]
在这种情况下,最小移动数将是移动:
(1, 2), (0, 2), (0, 2), (1, 0)
这将适用于所有情况。
因此,该问题已经简化为寻找将球门件放置在球门高度处或上方的最小移动次数的问题。
这将问题分解为一系列子问题:
当目标堆栈具有其可访问的棋子!=目标棋子时,确定该棋子是否存在有效位置,或者在交换另一棋子时该棋子是否应留在该位置。
当目标堆栈具有其可访问的棋子==目标棋子时,确定是否可以将其删除并放置在所需的目标高度,或者在交换另一个棋子时该棋子是否应保留。
当以上两种情况需要交换另一块时,确定要交换的块数才能增加,以使目标块达到目标高度。
目标堆栈应始终首先评估其大小写。
IE浏览器
stacks = [ [-, R, G], [-, R, G], [-, R, G] ]
Goal = stacks[0][1] = G
首先检查目标堆栈会导致:
(0, 1), (0, 2), (1, 0), (2, 0) = 4 Moves
忽略目标堆栈:
(1, 0), (1, 2), (0, 1), (0, 1), (2, 0) = 5 Moves