{"id":1288,"date":"2020-06-07T13:54:37","date_gmt":"2020-06-07T20:54:37","guid":{"rendered":"http:\/\/209.126.2.187\/?p=1288"},"modified":"2024-01-20T18:57:21","modified_gmt":"2024-01-21T02:57:21","slug":"shortest-path","status":"publish","type":"post","link":"https:\/\/nanzhou.cc\/index.php\/2020\/06\/07\/shortest-path\/","title":{"rendered":"Shortest Path"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">Summary<\/h2>\n\n\n\n<p>A common problem in undirected or directed graph is to calculate the shortest path between two nodes. There are several typical technologies that we could use. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Floyd-Warshall<\/strong> <\/h2>\n\n\n\n<p>Shortest paths (multiple sources) in such graphs that: <\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Edges are undirected or directed;<\/li>\n\n\n\n<li>Edges have positive or negative weights;<\/li>\n\n\n\n<li>No circles have negative weights; <\/li>\n<\/ol>\n\n\n\n<p>It is actually a DP problem. We define <code>alg[k][x][y]<\/code> as the shortest path from <code>x<\/code> to <code>y<\/code> using the nodes <code>[k, n-1]<\/code>. <\/p>\n\n\n\n<p>We have <code>f[k][x][y] = min(f[k+1][x][y], f[k+1][x][k]+f[k+1][k][y]<\/code>.  We can&#8217;t visit k twice since it would make the path longer than the first visit (circles are of positive weights). <\/p>\n\n\n\n<p>As an initiation, <code>f[n][x][y]=edge[x][y]<\/code>. We noticed that the first dimension could be eliminated. <\/p>\n\n\n\n<p>The time complexity will be <code>O(V^3)<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"cpp\" class=\"language-cpp line-numbers\">for (int i = 1; i &lt;= n; ++i) {\n  for (int j = 1; j &lt;= n; ++j) {\n    if (i==j) alg[i][j]=0;\n    else if (edge[i].count(j)) alg[i][j]=edge[i][j];\n    else alg[i][j]=INT_MAX;\n  }\n}\nfor (int k = n; k >= 1; --k) {\n  for (int i = 1; i &lt;= n; ++i) {\n    for (int j = 1; j &lt;= n; ++j) {\n      alg[i][j] = min(alg[i][j], alg[i][k] + alg[k][j]);\n    }\n  }\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Bellman-Ford<\/h2>\n\n\n\n<p>Shortest paths (single source) in such graphs that: <\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Edges are undirected or directed;<\/li>\n\n\n\n<li>Edges have positive or negative weights;<\/li>\n\n\n\n<li>Report paths not exist if there are negative cycles;<\/li>\n<\/ol>\n\n\n\n<p>Bellman-Ford is super useful if the problem limits the number of stops in the paths. <\/p>\n\n\n\n<p>It is a DP problem as well. We define <code>alg[i][k]<\/code> as the shortest path from <code>src<\/code> to any node <code>i<\/code> using <code>k<\/code> edges. <\/p>\n\n\n\n<p> As an initiation, we have <code>alg[src][0]=0<\/code> and all other values equal to <code>INT_MAX<\/code>.<\/p>\n\n\n\n<p>We have <code>alg[i][k]=min(alg[i][k-1], min{alg[j][k-1] + cost[j][i]})<\/code>. To obtain the value of <code>min{alg[j][k-1] + cost[j][i]}<\/code>, we can simply iterate all edges. <\/p>\n\n\n\n<p>The time complexity will be <code>O(VE)<\/code>. Since at most we will use <code>|V|-2<\/code> stops from source to destination.  <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"cpp\" class=\"language-cpp line-numbers\">\/\/ e[0] -&gt; e[1]: costs e[2] dollars\nvector&lt;vector&lt;int&gt;&gt; alg(n, vector&lt;int&gt;(K + 1, INT_MAX \/ 2));\nalg[src][0] = 0;\n\nfor (int k = 1; k &lt;= K; ++k) {\n    for (auto const &amp;e : edges) {\n\/\/ alg[e[1]][k] = min(alg[e[1]][k - 1], alg[e[0]][k - 1] + e[2]) is \n\/\/ incorrect, since the previous edge could update alg[e[1]][k]\n        alg[e[1]][k] = min(alg[e[1]][k], alg[e[1]][k - 1]);\n        alg[e[1]][k] = min(alg[e[1]][k], alg[e[0]][k - 1] + e[2]);\n    }\n}<\/code><\/pre>\n\n\n\n<p>If we don&#8217;t need to limit the steps. An 1d table is sufficient. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"cpp\" class=\"language-cpp line-numbers\">vector&lt;int&gt; dist(n, INT_MAX);\ndist[src] = 0;\n\nfor (int k = 1; k &lt;= n; ++k) {\n    for (auto const &amp;e : edges) {\n        if (dist[e[0]] != INT_MAX &amp;&amp; dist[e[0]] + e[2] &lt; dist[e[1]]) {\n            dist[e[1]] = dist[e[0]] + e[2];\n        }\n    }\n}<\/code><\/pre>\n\n\n\n<p>To detect if there are negative cycles, another loop to iterate each edge after <code>|V|<\/code> iterations could do the trick. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"cpp\" class=\"language-cpp line-numbers\">for (auto const &amp;e : edges) {\n   if (dist[e[0]] + e[2] &lt; dist[e[1]]) {\n       return NEGATIVE_CYCLE_FOUND;\n   } \n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Dijkstra<\/h2>\n\n\n\n<p>Shortest paths (single source) in such graphs that: <\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Edges are undirected or directed;<\/li>\n\n\n\n<li>Edges have positive weights;<\/li>\n<\/ol>\n\n\n\n<p>Dijkstra&#8217;s algorithm uses <code>priority queue<\/code> and <code>greedy<\/code> strategy. <\/p>\n\n\n\n<p>We use a priority queue to stores the potential nodes we could visit and its current cost. At initialization stage, we source&#8217;s direct neighbors. We <strong>greedily<\/strong> choose the node with lowest cost to visit. <\/p>\n\n\n\n<p>In each iteration, the cost we get is guaranteed to be the shortest path from source to the current node, which is a property of <strong>greedy<\/strong> (the first visit will always have smaller cost than other costs in queue). <strong>Note that<\/strong>: Not like BFS, where we can mark node as visited when we push them in the queue,  a node is marked as visited (gains its shortest path) only when it gets out of the queue. <\/p>\n\n\n\n<p>We then visit the newly introduced edges regarding the current node and update the shortest path in the queue (to guarantee there are no duplicate nodes in the queue). <\/p>\n\n\n\n<p>The size of queue will be at most <code>O(V)<\/code>; each node might have at most <code>O(V)<\/code> edges; the graph has <code>O(E)<\/code> edges, we visit each node once. So the time complexity will be <code>O((E+V)logV)<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"cpp\" class=\"language-cpp line-numbers\"># Trick: use hash map and iterator to update the shortest path in the queue\n\nvector&lt;int&gt; Dijkstra(int n, int src, unordered_map&lt;int, unordered_map&lt;int, int&gt;&gt; weight) {\n    \/\/ shortest path\n    vector&lt;int&gt; dist(n, INT_MAX);\n    \/\/ {cost, node}\n    set&lt;pair&lt;int, int&gt;&gt; s;\n    vector&lt;iter&gt; s_iter(n, s.end());\n\n    \/\/ initialization\n    s.insert({0, src});\n    s_iter[src] = s.begin();\n    dist[src] = 0;\n\n    while (!s.empty()) {\n        \/\/ u is the current node\n        auto [cost, u] = *s.begin();\n        s.erase(s.begin());\n\n        \/\/ get the shortest path from src to u\n        dist[u] = cost;\n\n        \/\/ v is the next node\n        for (auto const &amp;[v, w] : weight[u]) {\n            \/\/ this check is a must, since if dist[v] is visited, s_iter[v] won't be valid\n            if (dist[v] != INT_MAX) {\n                continue;\n            }\n            int new_cost = cost + w;\n            if (s_iter[v] != s.end()) {\n                \/\/ update the cost in the priority queue if the new_cost is lower\n                if (new_cost &lt; s_iter[v]-&gt;first) {\n                    s.erase(s_iter[v]);\n                    s_iter[v] = s.insert({new_cost, v}).first;\n                }\n            } else {\n                s_iter[v] = s.insert({new_cost, v}).first;\n            }\n        }\n    }\n    return dist;\n}<\/code><\/pre>\n\n\n\n<p>The above implementation works well if <code>|E|&gt;&gt;|V|<\/code>. Actually in many problems we have <code>|E|\u2248|V|<\/code>, then we could apply lazy deletion to simplify the implementation. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"cpp\" class=\"language-cpp line-numbers\"># Trick: lazy deletion; push extra edges into queue and delete them when they are popped out\nvector&lt;int&gt; Dijkstra(int n, int src, unordered_map&lt;int, unordered_map&lt;int, int&gt;&gt; weight) {\n    \/\/ shortest path\n    vector&lt;int&gt; dist(n, INT_MAX);\n    \/\/ {cost, node}\n    set&lt;pair&lt;int, int&gt;&gt; s;\n\n    \/\/ initialization\n    s.insert({0, src});\n    s_iter[src] = s.begin();\n    dist[src] = 0;\n\n    while (!s.empty()) {\n        \/\/ u is the current node\n        auto [cost, u] = *s.begin();\n        s.erase(s.begin());\n        \n        \/\/ lazy deletion\n        \/\/ we have visited the node before and obtained the smallest cost\n        if (dist[u] &lt; INT_MAX) {\n            continue;\n        }\n\n        \/\/ get the shortest path from src to u\n        dist[u] = cost;\n\n        \/\/ v is the next node\n        for (auto const &amp;[v, w] : weight[u]) {\n            int new_cost = cost + w;\n            \/\/ optional\n            if (dist[v] &lt; INT_MAX) {\n                continue;\n            }\n            s.insert({new_cost, v}).first;\n        }\n    }\n    return dist;\n}<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Summary A common problem in undirected or directed graph is to calculate the shortest path between two nodes. There are several typical technologies that we could use. Floyd-Warshall Shortest paths (multiple sources) in such graphs that: It is actually a DP problem. We define alg[k][x][y] as the shortest path from x to y using the&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[48,66],"tags":[],"class_list":["post-1288","post","type-post","status-publish","format-standard","hentry","category-alg","category-graph-alg"],"_links":{"self":[{"href":"https:\/\/nanzhou.cc\/index.php\/wp-json\/wp\/v2\/posts\/1288","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nanzhou.cc\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nanzhou.cc\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nanzhou.cc\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/nanzhou.cc\/index.php\/wp-json\/wp\/v2\/comments?post=1288"}],"version-history":[{"count":34,"href":"https:\/\/nanzhou.cc\/index.php\/wp-json\/wp\/v2\/posts\/1288\/revisions"}],"predecessor-version":[{"id":1686,"href":"https:\/\/nanzhou.cc\/index.php\/wp-json\/wp\/v2\/posts\/1288\/revisions\/1686"}],"wp:attachment":[{"href":"https:\/\/nanzhou.cc\/index.php\/wp-json\/wp\/v2\/media?parent=1288"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nanzhou.cc\/index.php\/wp-json\/wp\/v2\/categories?post=1288"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nanzhou.cc\/index.php\/wp-json\/wp\/v2\/tags?post=1288"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}