Johnson

Johnson

Johnson算法是一种Donald B. Johnson在1977年发布的:在边加权有向图中找到所有顶点对之间最短路径的算法。

jonhnson允许某些边权重为负数,但不能是负环
通过使用Bellman-Ford 算法来计算输入图的转换,以消除所有负权重,从而允许在转换后的图上使用Dijkstra 算法。

Suurballe算法中也使用了类似的重加权技术,用于在具有非负边权重的图形中查找相同两个顶点之间最小总长度的两条不相交路径。

算法步骤

  1. 将新节点 \(q\) 添加到图中,通过零权重边连接到其他每个节点。
  2. 使用\(Bellman-Ford\)算法,从新的顶点 \(q\) 开始,为每个顶点 \(v\) 查找从 $q $到 \(v\) 的路径的最小权重 \(h(v)\)。如果此步骤检测到负环,则终止算法使\(w(u,v) + h(s) \ge h(s)\)
    4 .使用\(Bellman-Ford\)计算的值重新加权原始图形的边缘:从\(u\)\(v\)的边,具有长度\({\displaystyle w(u,v)}\),给出新的长度 \(w(u,v) + h(u) − h(v)\)
    3 删除 \(q\),并使用 \(Dijkstra\) 算法查找从每个节点$ s$ 到重新加权图中其他每个顶点的最短路径。然后计算原始图中每个距离\(D(u,v)\)的距离,方法是将\(h(v) − h(u)\)添加到\(Dijkstra\)算法返回的距离中。

正确性

节点\(s\)\(t\)中之间的所有路径都添加了\(h(s) - h(v)\),设\(p\)\(s-t\)路径,它在加权后的权重\(w\)由下表示

\[(w(s,p_1)+h(s) - h(p_1)) + (w(p_1,p_2) + h(p_1)-h(p_2)) + \dots + (w(p_n,t) + h(p_n)-h(t)) \]

每个\(w_x\)中的 后一项 和\(w_{x+1}\)前一项的消去

\[w(s,p_1) + w(p_1,p_2) + \dots + w(p_n,t) + h(s) - h(t) \]

由于重新加权增加了相同的权重\({\displaystyle s-t}\),路径是原始加权图中的最短路径,当且仅当它是重新加权后的最短路径。属于从\(q\)到任何节点的最短路径的边缘的权重为\(0\),因此在重新加权图中,从\(q\)到每个节点的最短路径的长度变为\(0\);但是,它们仍然是最短的路径。因此,不可能有负边:如果边 \(uv\) 在重新加权后具有负权重,则从 \(q\)\(u\) 的零长度路径与此边一起将形成从 \(q\)\(v\) 的负权路径,这与所有顶点与$ q$ 的距离为零的事实相矛盾。负边的不存在确保了\(Dijkstra\)算法找到的路径的最优性。原始图中的距离可以通过加权变换,根据\(Dijkstra\)算法在重新加权图中计算的距离来计算。

#include <bits/stdc++.h>
using namespace std;
typedef  pair<int, int> pii;
const int N = 3e3 + 5;
int n, m, h[N], dis[N];
vector<pii> g[N];
int main() {
  cin >> n >> m;
  for(int w,u,v,i = 1; i <= m; i++)
   {
    cin >> u >> v >> w;
    g[u].push_back({v, w});
  }
  for(int i = 1; i <= n; i++) 
  {
    bool flag = 0;
    for(int j = 1; j <= n; j++)
      for(auto it : g[j])
        if(h[j] + it.second < h[it.first])
          flag = 1, h[it.first] = h[j] + it.second;
    if(i == n && flag) return !puts("-1");
  }
  for(int i = 1; i <= n; i++) 
  {
    long long ans = 0;
    for(int j = 1; j <= n; j++) dis[j] = i == j ? 0 : 1e9;
    priority_queue<pii, vector <pii>, greater <pii>> q;
    q.push({0, i});
    while(q.size()) 
    {
      auto t = q.top();q.pop();
      int id = t.second;
      if(t.first != dis[id]) continue;
      for(auto k : g[id]) 
      {
        int it = k.first, d = t.first + h[id] - h[it] + k.second;
        if(d < dis[it]) q.push({dis[it] = d, it});
      }
    }
    for(int j = 1; j <= n; j++) 
    ans += 1ll * j * (dis[j] + (dis[j] < 1e9 ? h[j] - h[i] : 0));
    cout << ans << endl;
  }
  return 0;
}

参考

初级图论
[johnson 维基百科](https://en.wikipedia.org/wiki/Johnson's_algorithm)

posted @ 2022-07-26 12:22  Erfu  阅读(395)  评论(0)    收藏  举报