网格图负权最短路

单身狗们大家节日快乐呢。

之前想把这篇拖到新年的时候发的,现在想想也不失为一种讽刺,因此只是按照时间把旧作发出来而已。

虽然希望自己是一个冷淡的人,不过大概还是没完全做到吧,旧的一年里,关心过帮助过我的人,非常感谢。

通常来说我特意许下的愿望都不怎么会实现,但这个愿望我相信应该会实现的——遇到能够改变自己的人,很期待这次相遇呢。如果实现不了的话感觉我这些无谓的挣扎就没什么意义了呢(笑)。

丧气的话不多说了,祝各位新年愉快。




  1. G 的节点数不大于 2 ,直接返回
  2. 沿着 G 的长边的中点把 G 分为 G_0,G_1
  3. 取中线上一点 r
  4. D_i=shortest-path(G_i,r)
  5. 算出 G_i 中任意两个中线上的点的距离 \delta_i[u][v]
  6. 对每个边界上的点 v 算出 B[v]=dist(r,v)
  7. 对每个点 v 算出 d'[v]=dist(r,v)
  8. 对每个点 v 算出 d[v]=dist(s,v)
  9. d 即为所求


Cost function

我们的算法主要基于 cost function 构建,事实上 Dijkstra 除了无法应对负权已经是足够优秀的算法了,为了能够让其处理负权,我们把原图中的距离 L(u,v) 映射到非负的 L_\phi(u,v)=L(u,v)+\phi(u)-\phi(v) 距离上,由于显然 L_\phi(u,v)+L_\phi(v,w)=L(u,v)+L(v,w)+\phi(u)-\phi(w) ,因此转换后的距离也是可以直接用 Dijkstra 求解的。

L_\phi(u,v) 非负需要选取合适的代价函数 \phi(v)

以 Step 8 为例,由于 dist(u,v)+dist(r,u)-dist(r,v)\geqslant 0 ,因此令 \phi(v)=d'[v] 即可。


Step 6


for v in V:
  e[0][v] = inf
e[0][r] = 0
for i in range(1, len(V) + 1):
  for v in V:
    e[i][v] = inf
      for w in V:
        e[i][v] = min(e[i][v], e[i - 1][w] + delta[i % 2][w][v])
for v in V:
  B[v] = e[len(V)][v]


先来看一段暴力,这段比较显然的暴力可以在 O(|V|^3) 的时间内求出结果。

考虑每一步转移,我们实际上是在找 \delta_{i\bmod 2}[w][v]+e[i - 1][w] 的列最小值。

如果一个 n\times n 的矩阵满足 Monge Property ( A_{i,k}+A_{j,l}\leqslant A_{i,l}+A_{j,k}\forall i<j,k<l ) 则我们可以在 O(|V|) 的时间里求出每一列的最小值。


注意到 Monge Property 保证了每列的最小值位置是单调不降的。

O(|V|\log |V|) : 暴力按决策单调性处理

O(|V|) : 注意到我们如果知道奇数列的最小值以后,偶数列的最小值是可以用一次扫描求出的,因此可以递归对所有奇数列求解,用以下的算法可以把 n\times m 的矩阵缩成 m\times m 的:

reduce(A):
	Q = A
	k = 1

	while row(Q) > m:
		if Q[k][k] < Q[k + 1][k]:
			if k < n: k += 1
			else: Q.deleteRow(m + 1)

		else:
			Q.deleteRow(k)
			if k > 1: k -= 1
	return Q

根据单调性的性质代码的正确性应该比较显然。


然而矩阵不一定满足 Monge Property ,当 i<k<j<l 的时候无法这么分析。

每次把矩阵切成 4 份,注意到右上和左下都满足 Monge Property ,对另两个递归。

总复杂度 O(n\log n)


Step 5


我们假定此处处理时满足边权非负,代价函数 \phi(v)=D_i(v)

我们顺着中线扫描,维护以每个点为根的最短路树,可以观察到每条边会在连续一段区间的最短路树上出现。

维护最短路树和对偶图上的树。每一条对偶图树的边对应了一条非树边,权值为 t(uv)=d(r,v)-d(r,u)-L_\phi(u,v)t(uv)>0 表示需要松弛。


计算 v_{i+1} 的最短路树的时候,首先把根从 v_i 移动过来,删除 v_{i+1} 的入边,连上 v_{i+1}v_i

然后进行松弛,令 P(e) 为对偶图树中 e^* 对应的路径,在对偶图树上查询 P(v_iv_{i+1}) 上的 t(uv) 最大的边,如果 t(uv)>0 就进行松弛(并对两棵树进行操作,删去 v 的入边,加上 uv ),松弛操作后给 P(e) 上减去 t(uv)ev 删掉的入边)。


由于每条边只会被操作常数次因此复杂度是 O(|V|\log |V|) ,单次询问是 O(\log n) 的。

在此基础上计算中线上任意两点的距离,复杂度 O(n\log n)

编辑于 2018-02-14

文章被以下专栏收录