Skip to content
Related Articles
Get the best out of our app
GFG App
Open App
geeksforgeeks
Browser
Continue

Related Articles

Monotonic shortest path from source to destination in Directed Weighted Graph

Improve Article
Save Article
Like Article
Improve Article
Save Article
Like Article

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

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

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.
  • 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;
}


Output

5.4

Time Complexity: O(N log(N) + M)
Auxiliary Space: O(N)


My Personal Notes arrow_drop_up
Last Updated : 18 Mar, 2023
Like Article
Save Article
Similar Reads
Related Tutorials