回答此问题可获得 20 贡献值,回答如果被采纳可获得 50 分。
<p><a href="https://i.stack.imgur.com/hT1RQ.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/hT1RQ.png" alt="Stacks"/></a></p>
<p>给定一组NXP堆栈,N表示堆栈数量,p表示堆栈容量,如何计算从位置a中的某个节点移动到任意位置B所需的最小交换数?我正在设计一个游戏,最终目标是对所有堆栈进行排序,使它们都是相同的颜色</p>
<pre><code># 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']
]
</code></pre>
<p>如果我想在<code>stacks[1][1]</code>处插入一个“B”,这样<code>stacks[1] = ["-", "B", "Y", "Y"]</code>。我如何确定这样做所需的最少移动次数</p>
<p>我一直在研究多种方法,我尝试了遗传算法,从一个状态生成所有可能的移动,对它们进行评分,然后继续沿着最佳评分路径前进,我还尝试运行Djikstra的算法来寻找问题的路径。这看起来简单得令人沮丧,但我想不出一种方法,让它在指数时间以外的任何时间运行。是否有一个我缺少的适用于这里的算法</p>
<h2>编辑</h2>
<p>我编写此函数是为了计算所需的最小移动次数:
堆栈:表示堆栈中各部分的字符列表,堆栈[0][0]是堆栈[0]的顶部
stack_ind:工件将添加到的堆栈的索引
所需工件:应添加到堆栈中的工件
需要索引:工件应位于的索引</p>
<pre><code>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")
</code></pre>
<p>编辑:
堆栈上的测试用例:</p>
<pre><code>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
</code></pre>
<p>实际的代码实现并不是困难的部分,它决定了如何实现一个算法来解决我正在努力解决的问题</p>
<p>根据@YonIif的请求,我为这个问题创建了一个<a href="https://gist.github.com/TristenHarr/16d49f594749f2068f706cd20a142ae3" rel="nofollow noreferrer">gist</a></p>
<p>当它运行时,它会生成一个随机堆栈数组,并选择一个需要插入随机堆栈中随机位置的随机片段</p>
<p>运行它会将这种格式的内容打印到控制台</p>
<pre><code>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']
</code></pre>
<h2>状态更新</h2>
<p>我决心以某种方式解决这个问题</p>
<p>请记住,有一些方法可以尽量减少案例数量,比如评论中提到的@Hans Olsson案例。我最近处理这个问题的方法是开发一组类似于上述规则的规则,并将它们应用到一个分代算法中</p>
<p>规则,例如:</p>
<p>永远不要改变一个动作。从1开始->;0然后0->;1(毫无意义)</p>
<p>不要连续两次移动工件。切勿从0移动->;1然后1->;三,</p>
<p>假设从堆栈[X]移动到堆栈[Y],然后移动一定数量,然后从堆栈[Y]移动到堆栈[Z],如果堆栈[Z]与从堆栈[X]移动到堆栈[Y]时处于相同的状态,则可以通过从堆栈[X]直接移动到堆栈[Z]来消除移动</p>
<p>目前,我正试图通过创建足够的规则来解决这个问题,这样可以最大限度地减少“有效”移动的数量,这样就可以使用分代算法来计算答案。如果有人能想出更多的规则,我很想在评论中听到他们</p>
<h2>更新</h2>
<p>多亏@RootTwo的回答,我有了一点突破,我将在这里概述一下</p>
<p><strong>取得突破</strong></p>
<p>将球门高度定义为球门片必须放置在球门中的深度
目标堆栈</p>
<p>每当某个球门被放置在索引处时<;=堆叠高度-目标高度,
通过clear_path()方法,总会有一条通往胜利的最短路径</p>
<pre><code>Let S represent some solid Piece.
</code></pre>
<p>即</p>
<pre><code>Stacks = [ [R, R, G], [G, G, R], [-, -, -] ]
Goal = Stacks[0][2] = R
Goal Height = 2.
Stack Height - Goal Height = 0
</code></pre>
<p>给一些堆栈<code>stack[0] = R</code>,游戏就赢了</p>
<pre><code> GOAL
[ [ (S | -), (S | -), (S | -) ], [R, S, S], [(S | - ), (S | -), (S | -)] ]
</code></pre>
<p>因为众所周知,它们总是至少是堆叠高度的空格
如果可用,最坏的情况可能是:</p>
<pre><code> [ [ S, S, !Goal ], [R, S, S], [-, -, -]
</code></pre>
<p>因为我们知道球门不可能在目的地,或者比赛就赢了。
在这种情况下,所需的最少移动次数为:</p>
<pre><code>(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
</code></pre>
<p>给定一些堆栈<code>stack[1] = R</code>,游戏是won</p>
<pre><code> GOAL
[ [ (S | -), (S | -), S], [ (S | -), R, S], [(S | -), (S | -), (S | -)]
</code></pre>
<p>我们知道至少有3个空格可用,因此最坏的情况可能是:</p>
<pre><code>[ [ S, !Goal, S], [S, R, S], [ -, -, - ]
</code></pre>
<p>在这种情况下,最小移动次数为:</p>
<pre><code>(1, 2), (0, 2), (0, 2), (1, 0)
</code></pre>
<p>这将适用于所有情况</p>
<p>因此,该问题已被简化为一个问题,即找到最小数量的
将球门放置在球门高度或以上所需的移动</p>
<p>这将问题分解为一系列子问题:</p>
<ol>
<li><p>当目标堆栈具有其可访问部分时!=球门,
确定该工件是否存在有效位置,或者工件是否应
在交换另一块时呆在那里</p></li>
<li><p>当目标堆栈具有其可访问片段==目标片段时,
确定是否可以将其移除并放置在所需的目标高度,或者
交换另一件时,该件应保留</p></li>
<li><p>当上述两种情况需要更换另一件时,
确定要交换哪些部件,以便增加以使
球门片达到球门高度</p></li>
</ol>
<p>目标堆栈应始终首先计算其案例</p>
<p>即</p>
<pre><code>stacks = [ [-, R, G], [-, R, G], [-, R, G] ]
Goal = stacks[0][1] = G
</code></pre>
<p>首先检查目标堆栈会导致:</p>
<pre><code>(0, 1), (0, 2), (1, 0), (2, 0) = 4 Moves
</code></pre>
<p>忽略目标堆栈:</p>
<pre><code>(1, 0), (1, 2), (0, 1), (0, 1), (2, 0) = 5 Moves
</code></pre>