这是一个家庭作业问题(对不起,我知道这些都是不受欢迎的),但是我和老师都不知道如何有效地解决它,所以我想把它放在这里,看看SO上的精彩大脑是否可以帮助我们。
给出了一个未指定大小的数组,其中包含随机数。它必须按递增顺序排序。每个元素可以移动到相邻的空白空间,也可以移动到相邻的较大元素的顶部。我需要编写一个方法来返回给定数组排序所需的最小移动次数。
这个问题被标记为“可选”,因为老师意识到这个问题太难了,但我很好奇它是如何解决的。对任何大小的数组的任何建议(对于我所关心的所有数组都可以是长度为3的数组)。
编辑:感谢您指出这一点尚不清楚。我正在使用数组来表示假设的现实世界情况。让我们使用硬币的例子:它们全部放在一张桌子上,并且只有一定数量的“空间”可以放入。但是它们可以放在相邻的较大硬币之上,或者滑动到一个相邻的空地(已被一枚可能在一堆上移动的硬币腾空)。答案 0 :(得分:1)
我决定用一些假设/变化来检验这个问题,因为这对我来说是一个更有趣的问题:
1)您可以从堆的任何部分向左或向右移动元素。
2)无论是更大,更小还是相同的尺寸,您都可以将元素堆叠在一堆上。
3)只要你在一个较小的数字之前从未遇到过更大的数字,无论你如何通过堆栈,数组都被认为是有序的。所以_ 11 2 3已经排序,但是_ _ 12 3并不是因为你可以将2解释为在1之前。
这导致了一个非常有趣的问题,尽管有这个公理:
Axiom A:你做出动作的顺序是无关紧要的,并且可以以任何方式重新安排以达到相同的最终结果。
Axiom Ab:如果数组中没有重复,那么只需将每个元素移动到最终位置。
特别是,我制定了一个策略,希望你只用本地检查就可以解决它,没有递归/回溯,但我已经证明这是徒劳无功的,并会在以后显示出来。
我的策略是:
1)从左到右继续寻找以错误方式翻转的对(较低数字前的较高数字)。
2a)当你找到一个时,如果有一个空白点或一个右手值可以立即填充的堆栈,请向左移动直到它填满它。
2b)否则,将左侧值向右移动一个。这会产生一种情况,即你有一堆无关紧要的数字 - 首先,在向下比较之前,根据1的逻辑将你移动的值与新右边的值进行比较。
2bii)以与对比较相同的方式处理向下比较 - 如果它可以适合空点或堆栈,则向左移动较低值,否则向右移动较高值并继续。
示例:
1231 - >我们向左移动1b因为它适合堆叠。 11 2 3 _
1321 - >我们向右移3,因为2不适合空位/堆栈。我们向左移动1b两次,因为它将适合一个空点/适合堆栈,然后我们向右移动3,因为2不适合空位/堆栈。 1 1 2 3
1132 - >我们向右转3,因为2不能左转。我们向左移动2,因为它适合空位。 1 1 2 3
2311 - >我们向右移动3,因为1a不能向左移动。我们将1b左移两次因为它适合空位。我们将1a向左移动,因为它会叠加。我们向右移动2因为1a1b不能向左移动。我们向左移动1a2b,因为它将填补一个空位。 11 2 3 _
但是,我们遇到了这两个起始数组的问题:
23411 10移动最佳,2r 3r 4r 1al * 3 1bl * 4使11 2 3 4 _。
23111 9移动最佳,2r * 3 3r * 3 1bl 1cl * 2使_ _ 111 2 3 - 与23411策略相反!我们减少了1s和23次,因为有更多的1,所以我们保存移动它们尽可能少。
这表明我们不能只进行简单的局部比较来解决这个新问题。
我仍然在考虑这个问题,以一种有趣的方式显得非常微不足道,我希望你们中的一些人喜欢和我一起思考这个问题:)
答案 1 :(得分:0)
我假设:
然后,如果数组包含多于一次的数字,则显然没有解决方案。此外,数字的大小无关紧要,只有它们的排名。因此,在不失一般性的情况下,我们可以假设数组包含任意顺序的数字1..n。此外,为了确定可能的移动,只有堆栈底部的顶部很重要。因此,存储足够了:
int[] bottom;
int[] top;
由于我们不能将堆栈分开,我们可能只将堆栈i移动到堆栈j上bottom[i] + 1 == top[j]
,否则我们将最终处于无法解决的状态。但是,如果我们处于可以进行此类移动的游戏状态,则最好立即执行此操作,因为最终我们必须组合这些堆栈,并且移动单个堆栈比移动两个堆栈更便宜。 / p>
因此,唯一的自由度是如何以最少的移动次数将堆栈移动到位。此时,我没有看到明显的贪婪算法,因此找到最佳解决方案可能需要将游戏的位置编码为图形,并将最短路径算法应用于该图形以找到最短的移动序列。
答案 2 :(得分:0)
编辑:鉴于每个人似乎都在解决不同的问题,我会陈述我正在解决的问题(我认为这是正确的问题(不是我们都是吗?) ):(使用磁盘有希望让事情更容易理解)
给定n个堆,每个堆只包含1个磁盘,按照大小的顺序排列这些磁盘。最终结果必须使每个堆包含1个磁盘。唯一允许的操作是将单个磁盘从一个堆的顶部移动到相邻堆的顶部,但受限于没有磁盘可以放置在较小的磁盘上。允许将磁盘移动到空堆或从堆中移动最后一个磁盘。总是有n堆,因此:(1)可能无法创建新桩,因此磁盘可能不会移动到原始序列的边界之外。 (2)空堆仍然存在,因此如果相邻位置为空,则在没有先移动到该位置的情况下,盘可能不会移动到该位置。
注意:强>
直径为x =数字x的磁盘。
这可能不是最有效的解决方案,但它应该起作用并且可能会让某人有所作为。
这是一个纯粹的迭代过程 - 在每一步之后,我们以所有大小为1的堆栈结束;这种方法可能是一种根本的低效率。
<强>解决方案:强>
我将使用1,2和5来说明以下内容,但这仅用于表示大小排序,对于具有相同排序的任何其他数字,它保持相同。
5
)512...
- 向右移动1
,向右移动5
,向左移动1
2,以152
结束521...
- 向左移动1
2,向右移动2
,向右移动1
2,向右移动5
,移动1
2向左,以152
...251...
- 向左移动1
2,向右移动5
,向右移动1
,以215
结束...152...
- 向左移动2
,向右移动1
,向右移动1
,向左移动2
,以251
结束(之后)我们从之前的案例中采取步骤)编辑2:
更有效的解决方案:
可能的预处理步骤:排序到单独的列表以有效地查找最小元素
优化:如果您要将磁盘向右移动超过其目标位置,请不要在上一步中将磁盘向右移动,而只是放置该磁盘上的这个磁盘。如何在合适的时间有效地做到这一点可能有点复杂。不执行某些措施以提高效率也可能有所帮助。
示例:强>
52413
// put smallest disk (1) on disk to the right
1
524_3
// shift disks to the left 1 position right (3 moves - moved 4, then 2, then 5)
1
_5243
// move 1 all the way left (4 moves)
15243
// same as above for 2
2
15_43
2
1_543
12543
当最小的磁盘位于最右侧位置时(现在是3
的情况),这有点问题。 2种解决方法:
交换1
和2
并将1
放在2
上1
2个位置,2
左,{{{ 1}}剩下2个位置)。那么你就有一个可以移动1
的空位。如果少于2个较小的元素,则不是一个选项。在我们对相同的情况进行分类之前,我们不应该修复这些元素。
如果我们有3
,我们可以简单地将12453
放在4
上,为5
打开一个插槽(这会延迟问题)。如果第一个未排序的磁盘(在这种情况下为3
)大于第二个(5
),我们可以使用一些策略,就像我之前针对上面的解决方案所描述的那样,移动元素来满足这个条件