【发布时间】:2025-12-16 23:10:02
【问题描述】:
我正在使用具有“开”和“关”两种状态的正方形网格。我有一个相当简单的Connected Component Labeling 算法,它可以找到所有“ON”组件。通常,但不总是,只有一个“ON”组件。
我希望构建一种算法,该算法将开/关单元格矩阵、组件标签(可能格式化为单元格的哈希集列表)以及自标签形成以来已更改的单元格列表作为输入,并输出一个新的标签。显而易见的解决方案是从头开始重新计算,尽管这效率不高。通常,已更改的单元格列表会很小。
如果更改列表只是已打开的单元格,这很容易做到:
Groups G;
Foreach changed cell C:
Group U = emptygroup;
U.add(C);
Foreach Group S in G:
if (S contains a cell which is adjacent to C)
G.Remove(S);
U.UnionWith(S);
G.add(C);
但是,如果更改包含任何已关闭的单元格,我不知道该怎么做。请记住,所有 ON 单元格必须恰好是一个组的成员。因此,一种解决方案是获取与新关闭的单元格相邻的每个单元格,并查看它们是否相互连接(例如,使用 * 寻路)。这将产生 1-4 个连续组(除非该单元格是其组中唯一的单元格,因此有 0 个相邻单元格要检查,在这种情况下它会产生 0 个组)。然而,这仅比从头开始好一点,因为通常(但并非总是)将这些相邻的方格连接在一起就像找到一个连续的组一样困难(除非有人建议用一种聪明的方法来做到这一点)。另外,如果有很多变化的单元格,这有点可怕......虽然我承认通常没有。
上下文,对于那些坚持知道我为什么这样做的人: Nurikabe 谜题中的一条规则是,您可能只有一组连续的墙。上面是我试图解决以提高速度(并玩寻路)的问题的简化。基本上,我希望检查连续的墙壁,而不会浪费从以前的此类测试中获得的信息。我想看看我的求解器中有多少地方可以利用以前的信息来提高速度,因为当 O(f(Δ)) 时使用 O(f(N)) 算法似乎有点痛苦算法就足够了(N 是拼图的大小,Δ 是自上次运行算法以来所做的更改)。
分析确实表明改进这个算法会改变执行时间,但这是一个有趣而不是盈利的项目,所以这并不重要,除了能够来衡量更改是否有任何影响。
注意: 我省略了解释我当前的算法,但它基本上是通过在它找到的第一个 ON 方格上执行基于堆栈的Flood Fill 算法,然后检查是否还有更多 ON 方格(这意味着有多个,它不会费心去检查)。
编辑:增强想法:Yairchu 和 John Kugelman 的建议在我的脑海中形成了这个改进,这实际上并不是这个问题本身的解决方案,但可能会使这部分代码和其他几段代码运行得更快:
当前循环:
foreach (Square s in m.Neighbors[tmp.X][tmp.Y])
{
if (0 != ((byte)(s.RoomType) & match) && Retval.Add(s)) curStack.Push(s);
}
改进思路:
foreach (Square s in m.NeighborsXX[tmp.X][tmp.Y])
{
if (Retval.Add(s)) curStack.Push(s);
}
这将需要维护多个 m.NeighborsXX 实例(一个用于需要增强的每种匹配类型)并在正方形发生变化时全部更新它们。我需要对此进行基准测试,看看它是否真的有帮助,但它看起来像是用一些内存换取一些速度的标准案例。
【问题讨论】:
标签: algorithm language-agnostic path-finding