我不知道如何简洁地描述目标,这可能就是为什么我无法找到适用的算法,尽管搜索量很大,但是图片清楚地显示了这一点:
鉴于左侧网格中的项目状态,是否有人知道有效查找右侧网格中显示的结束位置的算法?在这种情况下,所有项目都“堕落” “”向下“,但当然的方向是任意的。重点是:
这不是作业,我不是学生。这是出于我对几何和编程的兴趣。我没有提到这种语言因为没关系。我可以使用我正在使用的语言中的任何算法来处理我正在处理的特定项目。一个有用的答案可以用文字或代码来描述;这是重要的想法。
这个问题可能被抽象成依赖性和松弛空间的某种图形(在数学意义上),因此可能会采用旨在最小化滞后时间的算法。
如果您不知道答案但是即将尝试在现场编写算法,请记住可能存在循环依赖关系,例如互锁粉红色(向后)“C”和蓝色“T” “形状。 T的部分低于C,C的部分低于T.如果通过几个部件的“环”锁定互锁物品,这将更加棘手。
适用算法的一些注意事项:由于我构建网格对象管理框架的方式,以下所有内容都非常简单快捷:
关于答案的说明: 马涅克先是暗示了这一点,但布鲁普斯提供了一个精彩的解释。我认为绝对的关键是所有移动相同数量的作品保持彼此之间的关系的洞察力,因此这些关系不必被视为。
对于人口稀少的电路板,额外的加速是将所有部件移位以消除完全空的行。计算空行并在一侧(“上方”)标识空行非常容易。
最后一点:我确实实现了bloops描述的算法,并进行了一些特定于实现的修改。它工作得很漂亮。
答案 0 :(得分:9)
如下定义冻结对象的集合:
触及底部的物体被冻结。
冻结躺在冻结物体上的物体。
直观地说,确切地说,冷冻物体已到达最终位置。调用非冻结对象活动。
声明:所有活动对象可以同时向下落入一个单元。
证明:当然,活动对象不会碰到另一个活动对象,因为它们相对于彼此的相对位置不会改变。活动对象也不会命中冻结对象。如果是这样,那么活动对象实际上是冻结的,因为它躺在一个冻结的物体上。这与我们的假设相矛盾。
我们的算法非常高级的伪代码如下:
while (there are active objects):
move active objects downwards simultaneously until one of them hits a frozen object
update the status (active/frozen) of each object
请注意,在while循环的每次迭代中,至少有一个对象被冻结。此外,每个对象都会被冻结一次。在分析实际算法的运行时复杂度时,将使用这些观察结果。
我们使用 time 的概念来提高大多数操作的效率。从0开始测量时间,活动对象的每个单位移动需要1个单位时间。请注意,当我们处于t
时,当前在t
处当前处于活动状态的所有对象的位移正好向下t
个单位。
请注意,在每列中,每个单元格的相对顺序是固定的。其中一个含义是每个细胞可以直接阻止其他一个细胞落下。该观察结果可用于有效地预测下一次碰撞的时间。我们也可以最多一次“处理”每个单元格。
我们将列从1开始索引并从左到右递增;高度从1开始的行。为了便于实现,引入一个名为bottom
的新对象 - 这是唯一一个最初被冻结的对象,由高度为0的所有单元组成。
数据结构 为了实现高效,我们维护以下数据结构:
包含每个单元格的最终位移的关联数组A
。如果某个单元格处于活动状态,则其输入应为-1
。
对于每列k
,我们维护列S_k
中活动单元格的初始行数的集k
。我们需要能够支持此集合上的后续查询和删除。我们可以使用Van Emde Boas树,并回答O(log log H)
中的每个查询;其中H
是网格的高度。或者,我们可以使用平衡的二叉搜索树,它可以在O(log N)
中执行这些操作;其中N
是列k
中的单元格数。
优先级队列Q
,它将使用其密钥存储活动单元作为其未来冲突的预期时间。同样,我们可以使用查询时间为O(log log H)
的vEB树或每次操作O(log N)
时间的优先级队列。
<强>实施强>
该算法的详细伪代码如下:
Populate the S_k's with active cells
Initialize Q to be an empty priority queue
For every cell b in bottom:
Push Q[b] = 0
while (Q is not empty):
(x,t) = Q.extract_min() // the active cell x collides at time t
Object O = parent_object(x)
For every cell y in O:
A[y] = t // freeze cell y at displacement t
k = column(y)
S_k.delete(y)
a = S_k.successor(y) // find the only active cell that can collide with y
if(a != nil):
// find the expected time of the collision between a and y
// note that both their positions are currently t + (their original height)
coll_t = t + height(a) - height(y) - 1
Push/update Q[a] = coll_t
任何对象的最终位置都可以通过查询A
来获取属于该对象的任何单元格的位移来获得。
我们处理并冻结每个细胞一次。我们执行恒定数量的查找,同时冻结每个单元格。我们假设parent_object
查询可以在恒定时间内完成。整个算法的复杂性为O(N log N)
或O(N log log H)
,具体取决于我们使用的数据结构。这里,N
是所有对象的单元格总数。
答案 1 :(得分:6)
现在完全不同了:)
搁在地上的每件都是固定的。搁置在固定件上的每件都是固定的。其余的可以移动。将未固定的部件向下移动1个方向,重复直到无法移动。
答案 2 :(得分:3)
我还没有清除所有细节,但我认为以下似乎是一种有点系统的方法:
第三部分是唯一一个看似计算密集的部分 - 如果你有一个非常复杂的结构,但它仍然比试图一次移动一个方格的网格更优化。
答案 3 :(得分:3)
好的所以这看起来如下 -
1)如果x和y是两个物体,那么如果x不能移动直到y移动,则添加边x-> y。请注意,我们可以同时具有x-> y和y-> x边缘。
2)进一步有些物体因为它们位于底部而无法再移动,所以我们将它们的顶点着色为蓝色。其余顶点为红色。
2)在有向图中,我们使用Kosaraju算法/ Tarjan算法等找到所有强连通组件。(如果你不熟悉SCC,那么它们是非常强大的技术,你可以参考Kosaraju的算法。)一旦我们得到SCC我们将它们缩小到一个小顶点,即我们用一个顶点替换SCC,同时保留所有外部(到SCC)边缘。现在,如果SCC中的任何顶点是蓝色,那么我们将新顶点着色为蓝色,否则为红色。这意味着如果一个对象无法在SCC中移动,则无法移动。3)你得到的图表将是一个有向无环图,你可以进行拓扑排序。以顶部编号的递增顺序遍历顶点,只要您看到红色顶点并移动顶点表示的对象。
继续此步骤,直到您无法再在步骤3中移动任何顶点。
如果两个物体A和B重叠,那么我们说它们相对于彼此是不一致的。为了证明正确性,请考虑以下引理 1)“如果我移动SCC,那么它中的任何一个对象都不会引起它们之间的不一致。” 2)“当我在步骤3中移动对象时,我不会导致不一致”
现在面临的挑战是正式证明正确性并找到合适的数据结构来有效地解决它。如果您需要任何帮助,请告诉我。
答案 4 :(得分:2)
多次编辑。我认为这就是你需要做的所有事情:
找出所有只能相互依赖的作品,并将它们组合成一个相等的较大作品(例如图片中的T
和向后C
。)
迭代所有碎片,在撞击之前向下移动最大方向。重复,直到没有任何动作。