Skip to content

算法Algorithm

简单多边形最短路的漏斗算法

Funnel algorithm

在三角形通道上保存两条凹边界链,单向删点与移动 apex,在线性工作量内拉紧欧氏最短路。

形式陈述 ​

在带标准 Euclidean 内积的实平面中,给定无孔简单多边形 P、内部两点 s,t 及一份不加内部顶点的三角剖分,怎样求 P 内的 Euclidean 最短路?这里允许沿边界走。先用耳切或单调分块得到剖分;已知剖分后的漏斗阶段只需要线性时间。

把每个三角形看成一个节点,共有对角线的两片相连。该图连通,并有 n−2 个节点、n−3 条边,所以是树。起终点所在三角形之间只有一条简单路径。它经过的公共边依次称为门户,门户把路径需要穿过的三角形连成一个通道。

最短路不会离开通道后再回来。若它越出通道边界对角线,之后必须再次穿回同一条分隔对角线;把两次交点之间的绕行改成对角线上的直段,不会更长,非直绕行严格更短。因此只需在这条通道里拉紧路径。

每个门户写成 (ℓ,r),从已经经过的三角形看,ℓ 在左、r 在右:等价地,旧三角形第三点在有向边 ℓ→r 的右侧。相邻门户共享一个端点。最后把目标点视为最后三角形内新增的门户端点。

直觉

维护三份状态:已经确定的共同路径 tail,以及从其末点 a 分别通到当前门户左右端点的链 L,R。a 称为 apex。沿 L 的内部转弯向左,沿 R 的内部转弯向右;这两条链和门户围成一个扇状可见区域。到门户任意一点的最短路,先走共同 tail,再沿其中一条边界链走一段,最后直达该点。

新门户只替换一个端点。若新端点还能由当前 apex 看见,便收紧相应一侧;如果越过另一侧的第一条射线,就必须先绕过那一侧的第一个拐点。这个拐点从“可能要经过”变成“所有后续路径都必须经过”,于是移入 tail,成为新 apex。

两条链的收紧与 apex 前移
例子与边界

不重新扫描旧门户的链算法 ​

两条链用双端队列保存:链尾按后进先出顺序弹出,链首用于移动 apex。tail 最后一项与两链首项是同一个点。以下是替换左端为 v 的完整规则;右端规则镜像对称。

  1. 只要 L 至少有两点,且 orient(L−2,L−1,v)≤0,弹掉 L 的尾点
  2. 若 L 只剩 apex,检查另一链:只要 R 至少有两点,且 orient(R0,R1,v)<0,删除 R 的旧首点,把新首点加入 tail,并让 L 的唯一点改成新 apex
  3. 若 v 不等于当前左端,将 v 加到 L 尾部

替换右端时,第一步条件改为 orient(R−2,R−1,v)≥0;第二步改为 orient(L0,L1,v)>0,从 L 首端推进 apex。完全相同的端点不重复插入。共线时合并不必要的链尾点,只有严格越过另一侧才移动 apex。

这些方向判断对应两种几何修复:尾端弹出删掉已经可以直连跨越的旧拐点;首端弹出则确认必须绕过的拐点。后者是推进最短路共同前缀,不是把所有已处理门户退回去再算一遍。

四个门户与两个真正拐点 ​

仍用耳切页的十边形,取 s=(1,5),t=(7,5)。那份剖分给出的门户依次为

((4,2),(0,0)),((6,2),(0,0)),((6,2),(8,0)),((6,6),(8,0)).

第一步 L=[s,(4,2)]、R=[s,(0,0)]。第二门户把 (6,2) 压到左链,得到 L=[s,(4,2),(6,2)],右链不变。

第三门户把右端改成 (8,0)。先弹掉旧右端 (0,0),再发现新右端越过左链第一条射线,必须先经过 (4,2)。于是 tail 变为 [s,(4,2)],两链变为

L=[(4,2),(6,2)],R=[(4,2),(8,0)].

第四门户把 (6,6) 加入左链。处理最终目标 t 时,(6,6) 被弹掉,而 (6,2) 留下。输出 tail 去掉重复 apex 后接最终左链,得到

s→(4,2)→(6,2)→t,ℓ=32+2+10≈9.404918.

核验程序记录六次新链点插入、三次删除、一次 apex 前移,旧门户重扫次数为零。独立可见图给出相同长度;每段也通过精确的自由区域检查。

推论与应用

链不变量说明每次删除都不会丢掉最短路:同侧非凸拐弯可由弦缩短,跨到另一侧则由通道边界强迫经过旧链的下一个拐点。剩余的尾与两条凹链继续描述到整个门户的最短路族,故归纳到目标点时得到最短路。

设通道有 k 个门户。每个新门户只带来一个新端点;它在对应链中插入一次,之后至多从尾端删除一次,或随着 apex 推进从首端删除。已删记录不再插回。这一按记录计费的摊还分析给出总链操作为 O(k),最终输出另花 O(h),其中 h≤k+2 是路径顶点数。状态空间为 O(k)。

从头计时,还需三角剖分、定位起终点、搜索对偶树。用单调划分剖分为 O(nlog⁡n),线性扫描定位与树搜索为 O(n),漏斗也为 O(n);采用简单耳切,前置成本则为 O(n2)。不能把“漏斗线性”写成任何剖分实现的端到端线性。

只保存左右两条射线、换 apex 后从某个旧门户重新开始的短实现,可能多次处理同一门户。它并不自动继承上述链记录的摊还证明。本页实现显式保留两条链,用删除计数证明线性。带孔区域的对偶图有环,任取一条通道只能得到该通道或同伦类别内的解,不能直接声称是全局最短。

参考资料
关系图谱15 个相邻概念 · 2 类关系

拖动节点调整位置。

显示关系

显示:依赖

  1. 前置三跳
  2. 前置二跳
  3. 前置一跳
  4. 当前条目
  5. 后续一跳
  6. 后续二跳
  7. 后续三跳
文字版关系按与当前条目的最短距离分组
类型化关系