Monotonic shortest path from source to destination in Directed Weighted Graph
Given a weighted directed graph with N vertices and M edges, a source src and a destination target, the task is to find the shortest monotonic path (monotonically increasing or decreasing) from the source to the destination. Output -1 if no monotonic path is possible.
Note: All weights are non-negative
Examples:
Input: N = 6, M = 9, src = 1, target = 2
edges = {{1, 3, 1.1}, {1, 5, 2}, {1, 6, 3.3}, {2, 5, 2.7},
{3, 4, 2}, {3, 5, 1.1}, {4, 2, 2.3}, {5, 6, 2.4}, {6, 2, 3}}Graph for first example
Output: 5.4
Explanation: There are three monotonic paths in the graph
that originate from vertex 1, which are 1 – 6 – 2 because it is strictly increasing,
and 1 – 3 – 4 – 2, and 1 – 5 – 6 – 2 since both are strictly decreasing.
The shortest one of these paths is 1 – 3 – 4 – 2,
which has a sum of weights equal to 1.1 + 2 + 2.3 = 5.4,
So the output is 5.4.Input: N = 5, M = 5, src = 1, target = 5
edges = {{1, 2, 2.3}, {1, 3, 3.1}, {2, 3, 3.7}, {3, 4, 1.9}, {4, 5, 2.1}}Graph for second example
Output: -1
Explanation: No monotonic path exists from vertex 1 to vertex 5.
Approach: To solve the problem follow the below idea:
Run Dijkstra’s algorithm twice: one for increasing shortest paths and another for decreasing shortest paths, and take the shorter path of the two results.
Follow the given steps to solve the problem:
- Run Dijkstra’s algorithm twice for both increasing and decreasing paths.
- While doing Dijkstra’s for decreasing shortest paths:
- Only update the shortest path to a vertex v from vertex u if the weight of the edge from u to v is less than the edge on the shortest path directed towards u.
- Similarly for the increasing shortest paths:
- Only update the shortest path to a vertex v from u, if the edge from u to v is greater than the edge on the shortest path directed towards u.
- While doing Dijkstra’s for decreasing shortest paths:
- If the destination vertex has not yet been reached, then no valid shortest path exists.
- If both passes of Dijkstra’s on increasing and decreasing shortest paths result in no valid paths, then return -1.
Below is the implementation of the above approach.
Java
import java.io.*; import java.util.*; // Finds the monotonic shortest path // using Dijkstra's algorithm public class Main { public static void main(String[] args) { int N = 6 ; int M = 9 ; // Create an array of vertices Vertex[] vertices = new Vertex[N + 1 ]; // Create instances of each vertex from 1 to N for ( int i = 1 ; i <= N; i++) vertices[i] = new Vertex(i); vertices[ 1 ].adjList.add( 3 ); vertices[ 1 ].adjWeights.add( 1.1 ); vertices[ 1 ].adjList.add( 5 ); vertices[ 1 ].adjWeights.add( 2.0 ); vertices[ 1 ].adjList.add( 6 ); vertices[ 1 ].adjWeights.add( 3.3 ); vertices[ 2 ].adjList.add( 5 ); vertices[ 2 ].adjWeights.add( 2.7 ); vertices[ 3 ].adjList.add( 4 ); vertices[ 3 ].adjWeights.add( 2.0 ); vertices[ 3 ].adjList.add( 5 ); vertices[ 3 ].adjWeights.add( 1.1 ); vertices[ 4 ].adjList.add( 2 ); vertices[ 4 ].adjWeights.add( 2.3 ); vertices[ 5 ].adjList.add( 6 ); vertices[ 5 ].adjWeights.add( 2.4 ); vertices[ 6 ].adjList.add( 2 ); vertices[ 6 ].adjWeights.add( 3.0 ); // Source and destination vertices int src = 1 ; int target = 2 ; System.out.println( shortestPath(vertices, N, src, target)); } public static double shortestPath(Vertex vertices[], int N, int source, int destination) { // Stores distance from source and edge // on the shortest path from source double [] distTo = new double [N + 1 ]; double [] edgeTo = new double [N + 1 ]; // Set initial distance from source // to the highest value for ( int i = 1 ; i <= N; i++) distTo[i] = Double.MAX_VALUE; // Monotonic decreasing pass of dijkstras distTo = 0.0 ; edgeTo = Double.MAX_VALUE; PriorityQueue<Vertex> pq = new PriorityQueue<Vertex>( new Comparator<Vertex>() { public int compare(Vertex a, Vertex b) { return Double.compare(distTo[a.id], distTo[b.id]); } }); // Add the initial source vertex // into the priority queue pq.add(vertices); while (!pq.isEmpty()) { // Take the vertex with the closest // current distance from source Vertex closest = pq.remove(); for ( int i = 0 ; i < closest.adjList.size(); i++) { // Checks if the edges are decreasing and // whether the current directed edge will // create a shorter path if (closest.adjWeights.get(i) < edgeTo[closest.id] && distTo[closest.id] + closest.adjWeights.get(i) < distTo[closest.adjList.get( i)]) { edgeTo[closest.adjList.get(i)] = closest.adjWeights.get(i); distTo[closest.adjList.get(i)] = closest.adjWeights.get(i) + distTo[closest.id]; pq.add( vertices[closest.adjList.get(i)]); } } } // Store the result of the first pass of dijkstras double firstPass = distTo[destination]; // Monotonic increasing pass of dijkstras for ( int i = 1 ; i <= N; i++) distTo[i] = Double.MAX_VALUE; distTo = 0.0 ; edgeTo = 0.0 ; // Add the initial source vertex // into the priority queue pq.add(vertices); while (!pq.isEmpty()) { // Take the vertex with the closest current // distance from source Vertex closest = pq.remove(); for ( int i = 0 ; i < closest.adjList.size(); i++) { // Checks if the edges are increasing and // whether the current directed edge will // create a shorter path if (closest.adjWeights.get(i) > edgeTo[closest.id] && distTo[closest.id] + closest.adjWeights.get(i) < distTo[closest.adjList.get( i)]) { edgeTo[closest.adjList.get(i)] = closest.adjWeights.get(i); distTo[closest.adjList.get(i)] = closest.adjWeights.get(i) + distTo[closest.id]; pq.add( vertices[closest.adjList.get(i)]); } } } // Store the result of the second pass of Dijkstras double secondPass = distTo[destination]; if (firstPass == Double.MAX_VALUE && secondPass == Double.MAX_VALUE) return - 1 ; return Math.min(firstPass, secondPass); } } // Represents a vertex in the graph // id stores the vertex number of the vertex instance // adjList stores the id's of adjacent vertices // adjWeights stores the weights of adjacent vertices with // the same indexing as adjList class Vertex { int id; ArrayList<Integer> adjList; ArrayList<Double> adjWeights; // A constructor which accepts // the id of the vertex public Vertex( int num) { id = num; adjList = new ArrayList<Integer>(); adjWeights = new ArrayList<Double>(); } } |
Python3
import heapq class Vertex: def __init__( self , id ): self . id = id self .adjList = [] self .adjWeights = [] def shortestPath(vertices, N, source, destination): # Stores distance from source and edge # on the shortest path from source distTo = [ float ( 'inf' ) for _ in range (N + 1 )] edgeTo = [ float ( 'inf' ) for _ in range (N + 1 )] # Set initial distance from source # to the highest value distTo = 0.0 edgeTo = float ( 'inf' ) pq = [( 0 , source)] heapq.heapify(pq) while pq: _, closest = heapq.heappop(pq) for i in range ( len (vertices[closest].adjList)): # Checks if the edges are decreasing and # whether the current directed edge will # create a shorter path if vertices[closest].adjWeights[i] < edgeTo[closest] and distTo[closest] + vertices[closest].adjWeights[i] < distTo[vertices[closest].adjList[i]]: edgeTo[vertices[closest].adjList[i]] = vertices[closest].adjWeights[i] distTo[vertices[closest].adjList[i]] = distTo[closest] + vertices[closest].adjWeights[i] heapq.heappush(pq, (distTo[vertices[closest].adjList[i]], vertices[closest].adjList[i])) return distTo[destination] N = 6 M = 9 # Create an array of vertices vertices = [Vertex(i) for i in range ( 1 , N + 1 )] vertices[ 0 ].adjList.append( 2 ) vertices[ 0 ].adjWeights.append( 1.1 ) vertices[ 0 ].adjList.append( 4 ) vertices[ 0 ].adjWeights.append( 2.0 ) vertices[ 0 ].adjList.append( 5 ) vertices[ 0 ].adjWeights.append( 3.3 ) vertices[ 1 ].adjList.append( 4 ) vertices[ 1 ].adjWeights.append( 2.7 ) vertices[ 2 ].adjList.append( 3 ) vertices[ 2 ].adjWeights.append( 2.0 ) vertices[ 2 ].adjList.append( 4 ) vertices[ 2 ].adjWeights.append( 1.1 ) vertices[ 3 ].adjList.append( 1 ) vertices[ 3 ].adjWeights.append( 2.3 ) vertices[ 4 ].adjList.append( 5 ) vertices[ 4 ].adjWeights.append( 2.4 ) vertices[ 5 ].adjList.append( 1 ) vertices[ 5 ].adjWeights.append( 3.0 ) # Source and destination vertices src = 1 target = 2 print (shortestPath(vertices, N, src, target)) |
Javascript
class Vertex { constructor(id) { this .id = id; this .adjList = []; this .adjWeights = []; } } function shortestPath(vertices, N, source, destination) { // Stores distance from source and edge // on the shortest path from source let distTo = new Array(N + 1).fill(Infinity); let edgeTo = new Array(N + 1).fill(Infinity); // Set initial distance from source // to the highest value distTo = 0.0; edgeTo = Infinity; let pq = [[0, source]]; pq.sort((a, b) => a[0] - b[0]); while (pq.length > 0) { let [_, closest] = pq.shift(); for (let i = 0; i < vertices[closest].adjList.length; i++) { // Checks if the edges are decreasing and // whether the current directed edge will // create a shorter path if (vertices[closest].adjWeights[i] < edgeTo[closest] && distTo[closest] + vertices[closest].adjWeights[i] < distTo[vertices[closest].adjList[i]]) { edgeTo[vertices[closest].adjList[i]] = vertices[closest].adjWeights[i]; distTo[vertices[closest].adjList[i]] = distTo[closest] + vertices[closest].adjWeights[i]; pq.push([distTo[vertices[closest].adjList[i]], vertices[closest].adjList[i]]); pq.sort((a, b) => a[0] - b[0]); } } } return distTo[destination]; } let N = 6; let M = 9; // Create an array of vertices let vertices = []; for (let i = 1; i <= N; i++) { vertices.push( new Vertex(i)); } vertices[0].adjList.push(2); vertices[0].adjWeights.push(1.1); vertices[0].adjList.push(4); vertices[0].adjWeights.push(2.0); vertices[0].adjList.push(5); vertices[0].adjWeights.push(3.3); vertices[1].adjList.push(4); vertices[1].adjWeights.push(2.7); vertices[2].adjList.push(3); vertices[2].adjWeights.push(2.0); vertices[2].adjList.push(4); vertices[2].adjWeights.push(1.1); vertices[3].adjList.push(1); vertices[3].adjWeights.push(2.3); vertices[4].adjList.push(5); vertices[4].adjWeights.push(2.4); vertices[5].adjList.push(1); vertices[5].adjWeights.push(3.0); // Source and destination vertices let src = 1; let target = 2; console.log(shortestPath(vertices, N, src, target)); |
C++
#include <bits/stdc++.h> #include <limits> #include <queue> #include <vector> using namespace std; // Represents a vertex in the graph class Vertex { public : int id; vector< int > adjList; vector< double > adjWeights; // A constructor which accepts the id of the vertex Vertex( int num) : id(num) { } }; // Finds the monotonic shortest path using Dijkstra's // algorithm double shortestPath(vector<Vertex>& vertices, int source, int destination) { int N = vertices.size() - 1; // Stores distance from source and edge on the shortest // path from source vector< double > distTo(N + 1, numeric_limits< double >::max()); vector< double > edgeTo(N + 1, numeric_limits< double >::max()); // Set initial distance from source to the highest value for ( int i = 1; i <= N; i++) { distTo[i] = numeric_limits< double >::max(); } // Monotonic decreasing pass of Dijkstra's distTo = 0.0; edgeTo = numeric_limits< double >::max(); priority_queue<pair< double , int >, vector<pair< double , int > >, greater<pair< double , int > > > pq; pq.push(make_pair(0.0, source)); while (!pq.empty()) { // Take the vertex with the closest current distance // from source pair< double , int > top = pq.top(); pq.pop(); int closest = top.second; for ( int i = 0; i < vertices[closest].adjList.size(); i++) { int neighbor = vertices[closest].adjList[i]; double weight = vertices[closest].adjWeights[i]; // Checks if the edges are decreasing and // whether the current directed edge will create // a shorter path if (weight < edgeTo[closest] && distTo[closest] + weight < distTo[neighbor]) { edgeTo[neighbor] = weight; distTo[neighbor] = distTo[closest] + weight; pq.push( make_pair(distTo[neighbor], neighbor)); } } } // Store the result of the first pass of Dijkstra's double firstPass = distTo[destination]; // Monotonic increasing pass of Dijkstra's for ( int i = 1; i <= N; i++) { distTo[i] = numeric_limits< double >::max(); } distTo = 0.0; edgeTo = 0.0; pq.push(make_pair(0.0, source)); while (!pq.empty()) { // Take the vertex with the closest current distance // from source pair< double , int > top = pq.top(); pq.pop(); int closest = top.second; for ( int i = 0; i < vertices[closest].adjList.size(); i++) { int neighbor = vertices[closest].adjList[i]; double weight = vertices[closest].adjWeights[i]; // Checks if the edges are increasing and // whether the current directed edge will create // a shorter path if (weight > edgeTo[closest] && distTo[closest] + weight < distTo[neighbor]) { edgeTo[neighbor] = weight; distTo[neighbor] = distTo[closest] + weight; pq.push( make_pair(distTo[neighbor], neighbor)); } } } // Store the result of the second pass of Dijkstras double secondPass = distTo[destination]; if (firstPass == DBL_MAX && secondPass == DBL_MAX) return -1; return min(firstPass, secondPass); } // Driver Code int main() { int N = 6, M = 9, src, target; // Create N vertices, numbered 1 to N vector<Vertex> vertices(N + 1, Vertex(0)); for ( int i = 1; i <= N; i++) { vertices[i] = Vertex(i); } // Add M edges to the graph vertices[1].adjList.push_back(3); vertices[1].adjWeights.push_back(1.1); vertices[1].adjList.push_back(5); vertices[1].adjWeights.push_back(2.0); vertices[1].adjList.push_back(6); vertices[1].adjWeights.push_back(3.3); vertices[2].adjList.push_back(5); vertices[2].adjWeights.push_back(2.7); vertices[3].adjList.push_back(4); vertices[3].adjWeights.push_back(2.0); vertices[3].adjList.push_back(5); vertices[3].adjWeights.push_back(1.1); vertices[4].adjList.push_back(2); vertices[4].adjWeights.push_back(2.3); vertices[5].adjList.push_back(6); vertices[5].adjWeights.push_back(2.4); vertices[6].adjList.push_back(2); vertices[6].adjWeights.push_back(3.0); // Source and destination vertices src = 1; target = 2; double shortest = shortestPath(vertices, src, target); cout << shortest << endl; return 0; } |
5.4
Time Complexity: O(N log(N) + M)
Auxiliary Space: O(N)
Please Login to comment...