Open In App
Related Articles

Shortest path from source to destination such that edge weights along path are alternatively increasing and decreasing

Improve
Improve
Improve
Like Article
Like
Save Article
Save
Report issue
Report

Given a connected graph with N vertices and M edges. The task is to find the shortest path from source to the destination vertex such that the difference between adjacent edge weights in the shortest path change from positive to negative and vice versa ( Weight(E1) > Weight(E2) < Weight(E3) …. ). If no such path exists then print -1. Examples:

Input: source = 4, destination = 3 Output: 19 4 – 2 – 1 – 3 (Edge Weights: 8, 3, 10) and 4 – 1 – 2 – 3 (Edge Weights: 6, 3, 10) are the only valid paths. Second path takes the minimum cost i.e. 19. Input: source = 2, destination = 4 Output: -1 No such path exists.

Approach: Here, We need to keep two copies of adjacent lists one for positive difference and other for negative difference. Take a Priority Queue as in Dijkstras Algorithm and keep four variables at a time i.e.,

  1. cost: To store the cost of the path till current node.
  2. stage: An integer variable to tell what element needs to be taken next, if the previous value was negative then a positive value needs to be taken else take negative.
  3. weight: Weight of the last visited node.
  4. vertex: Last visited vertex.

For every vertex push the adjacent vertices based on the required condition (value of stage). See the code for better understanding. Below is the implementation of the above approach: 

CPP

// C++ implementation of the approach
#include <bits/stdc++.h>
using namespace std;
#define N 100005
 
// To store the graph
vector<pair<int, int> > incr[N], decr[N];
int _incr[N], _decr[N], shortest[N];
 
int n, m, src, dest, MAXI = 1LL << 30;
 
// Function to add edges
void Add_edge(int x, int y, int w)
{
    incr[x].push_back({ w, y });
    incr[y].push_back({ w, x });
    decr[x].push_back({ -w, y });
    decr[y].push_back({ -w, x });
}
 
// Function to find the shortest distance from
// source to destination
int Modified_Dijkstra()
{
 
    // Total cost, stage, weight of previous, vertex
    priority_queue<pair<pair<int, int>, pair<int, int> > > q;
 
    // Sort the edges
    for (int i = 1; i <= n; i++) {
        sort(incr[i].begin(), incr[i].end());
        sort(decr[i].begin(), decr[i].end());
    }
 
    for (int i = 1; i <= n; i++)
        shortest[i] = MAXI;
 
    // Push the source vertex
    q.push({ { 0, 0 }, { 0, src } });
 
    while (!q.empty()) {
 
        // Take the top element in the queue
        pair<pair<int, int>, pair<int, int> > FRONT = q.top();
 
        // Remove it from the queue
        q.pop();
 
        // Store all the values
        int cost = -FRONT.first.first;
        int stage = FRONT.first.second;
        int weight = FRONT.second.first;
        int v = FRONT.second.second;
 
        // Take the minimum cost for the vertex
        shortest[v] = min(shortest[v], cost);
 
        // If destination vertex has already been visited
        if (shortest[dest] != MAXI)
            break;
 
        // To make difference negative
        if (stage) {
 
            // Start from last not visited vertex
            for (int i = _incr[v]; i < incr[v].size(); i++) {
 
                // If we can take the ith vertex
                if (weight > incr[v][i].first)
                    q.push({ { -(cost + incr[v][i].first), 0 },
                            { incr[v][i].first, incr[v][i].second } });
                else {
 
                    // To keep the last not visited vertex
                    _incr[v] = i;
                    break;
                }
            }
        }
 
        // To make difference positive
        else {
 
            // Start from last not visited vertex
            for (int i = _decr[v]; i < decr[v].size(); i++) {
 
                // If we can take the ith vertex
                if (weight < -decr[v][i].first)
                    q.push({ { -(cost - decr[v][i].first), 1 },
                            { -decr[v][i].first, decr[v][i].second } });
                else {
 
                    // To keep the last not visited vertex
                    _decr[v] = i;
                    break;
                }
            }
        }
    }
 
    if (shortest[dest] == MAXI)
        return -1;
 
    return shortest[dest];
}
 
// Driver code
int main()
{
    n = 5, src = 4, dest = 3;
 
    // Adding edges
    Add_edge(4, 2, 8);
    Add_edge(1, 4, 6);
    Add_edge(2, 3, 10);
    Add_edge(3, 1, 10);
    Add_edge(1, 2, 3);
    Add_edge(3, 5, 3);
 
    cout << Modified_Dijkstra();
 
    return 0;
}

                    

Java

/*package whatever //do not write package name here */
 
import java.util.*;
 
class PQPair {
    int[] first, second;
 
    public PQPair(int[] first, int[] second)
    {
        this.first = first;
        this.second = second;
    }
}
 
public class GFG {
    static final int N = 100005;
    static List<int[]> incr[], decr[];
    static int[] _incr, _decr, shortest;
    static int n, m, src, dest, MAXI = 1 << 30;
 
    public static void Add_edge(int x, int y, int w)
    {
        incr[x].add(new int[] { w, y });
        incr[y].add(new int[] { w, x });
        decr[x].add(new int[] { -w, y });
        decr[y].add(new int[] { -w, x });
    }
 
    public static int Modified_Dijkstra()
    {
 
        // Total cost, stage, weight of previous, vertex
        PriorityQueue<PQPair> q = new PriorityQueue<PQPair>(
            (a, b) -> b.first[0] - a.first[0]);
 
        // Sort the edges
        for (int i = 1; i <= n; i++) {
            Collections.sort(incr[i],
                             (a, b)
                                 -> a[0] != b[0]
                                        ? a[0] - b[0]
                                        : a[1] - b[1]);
            Collections.sort(decr[i],
                             (a, b)
                                 -> a[0] != b[0]
                                        ? a[0] - b[0]
                                        : a[1] - b[1]);
        }
 
        for (int i = 1; i <= n; i++)
            shortest[i] = MAXI;
 
        // Push the source vertex
        q.add(new PQPair(new int[] { 0, 0 },
                         new int[] { 0, src }));
 
        while (!q.isEmpty()) {
 
            // Remove the top element from the queue
            PQPair FRONT = q.remove();
 
            // Store all the values
            int cost = -FRONT.first[0];
            int stage = FRONT.first[1];
            int weight = FRONT.second[0];
            int v = FRONT.second[1];
 
            // Take the minimum cost for the vertex
            shortest[v] = Math.min(shortest[v], cost);
 
            // If destination vertex has already been
            // visited
            if (shortest[dest] != MAXI)
                break;
 
            // To make difference negative
            if (stage == 1) {
 
                // Start from last not visited vertex
                for (int i = _incr[v]; i < incr[v].size();
                     i++) {
 
                    // If we can take the ith vertex
                    if (weight > incr[v].get(i)[0])
                        q.add(new PQPair(
                            new int[] {
                                -(cost + incr[v].get(i)[0]),
                                0 },
                            new int[] {
                                incr[v].get(i)[0],
                                incr[v].get(i)[1] }));
 
                    else {
                        // To keep the last not visited
                        // vertex
                        _incr[v] = i;
                        break;
                    }
                }
            }
 
            // To make difference positive
            else {
                // Start from last not visited vertex
                for (int i = _decr[v]; i < decr[v].size();
                     i++) {
 
                    // If we can take the ith vertex
 
                    if (weight < -decr[v].get(i)[0])
                        q.add(new PQPair(
                            new int[] {
                                -(cost - decr[v].get(i)[0]),
                                1 },
                            new int[] {
                                -decr[v].get(i)[0],
                                decr[v].get(i)[1] }));
                    else {
                        // To keep the last not visited
                        // vertex
                        _decr[v] = i;
                        break;
                    }
                }
            }
        }
 
        if (shortest[dest] == MAXI)
            return -1;
 
        return shortest[dest];
    }
 
    public static void main(String[] args)
    {
        n = 5;
        src = 4;
        dest = 3;
        incr = new ArrayList[n + 1];
        decr = new ArrayList[n + 1];
        _incr = new int[n + 1];
        _decr = new int[n + 1];
        shortest = new int[n + 1];
        for (int i = 0; i <= n; i++) {
            incr[i] = new ArrayList<int[]>();
            decr[i] = new ArrayList<int[]>();
        }
 
        // Adding edges
        Add_edge(4, 2, 8);
        Add_edge(1, 4, 6);
        Add_edge(2, 3, 10);
        Add_edge(3, 1, 10);
        Add_edge(1, 2, 3);
        Add_edge(3, 5, 3);
 
        System.out.println(Modified_Dijkstra());
    }
}

                    

Python

import heapq
 
class PriorityQueue:
    def __init__(self):
        self.heap = []
     
    def push(self, item):
        heapq.heappush(self.heap, item)
     
    def pop(self):
        return heapq.heappop(self.heap)
     
    def is_empty(self):
        return len(self.heap) == 0
 
class PQPair:
    def __init__(self, first, second):
        self.first = first
        self.second = second
 
def shortest_path_with_change(adj_pos, adj_neg, src, dest):
    MAXI = float('inf')
    q = PriorityQueue()
    q.push(PQPair([0, 0], [0, src]))
    visited = set()
 
    while not q.is_empty():
        front = q.pop()
        cost, stage = -front.first[0], front.first[1]
        weight, v = front.second[0], front.second[1]
         
        if v == dest:
            return cost
         
        visited.add(v)
         
        if stage == 1: # Positive difference
            for w, u in adj_pos[v]:
                if weight < w and u not in visited:
                    q.push(PQPair([-cost - w, 0], [w, u]))
        else: # Negative difference
            for w, u in adj_neg[v]:
                if weight > w and u not in visited:
                    q.push(PQPair([-cost + w, 1], [w, u]))
     
    return -1
 
# Example usage:
adj_pos = {
    1: [(1, 2)],
    2: [(3, 3)],
    3: [(5, 4)],
    4: [(2, 5)],
    5: [(1, 6)],
    6: [(4, 7)],
    7: []
}
 
# Constructing the negative adjacency list
adj_neg = {}
for i in range(1, 8):
    adj_neg[i] = [(-w, u) for w, u in adj_pos[i]]
 
src, dest = 1, 7
 
# Finding the shortest path with changing differences
print(shortest_path_with_change(adj_pos, adj_neg, src, dest))

                    

C#

using System;
using System.Collections.Generic;
using System.Linq;
 
public class PriorityQueue<T>
{
    private List<T> heap;
    private Comparison<T> comparator;
 
    // Constructor
    public PriorityQueue(Comparison<T> comparator)
    {
        heap = new List<T>();
        this.comparator = comparator;
    }
 
    // Method to add an element to the priority queue
    public void Push(T value)
    {
        heap.Add(value);
        heap.Sort(comparator);
    }
 
    // Method to remove and return the element with the highest priority from the priority queue
    public T Pop()
    {
        T frontItem = heap.First();
        heap.RemoveAt(0);
        return frontItem;
    }
 
    // Method to check if the priority queue is empty
    public bool IsEmpty()
    {
        return heap.Count == 0;
    }
}
 
public class PQPair
{
    public int[] First { get; set; }
    public int[] Second { get; set; }
 
    // Constructor
    public PQPair(int[] first, int[] second)
    {
        First = first;
        Second = second;
    }
}
 
class Program
{
    // Constants and variables declaration
    const int N = 100005;
    static List<int[]>[] incr = Enumerable.Range(0, N).Select(_ => new List<int[]>()).ToArray();
    static List<int[]>[] decr = Enumerable.Range(0, N).Select(_ => new List<int[]>()).ToArray();
    static int[] _incr = new int[N];
    static int[] _decr = new int[N];
    static int[] shortest = new int[N];
    static int n, src, dest;
    static int MAXI = 1 << 30;
 
    // Function to add edges to the graph
    static void Add_edge(int x, int y, int w)
    {
        incr[x].Add(new int[] { w, y });
        incr[y].Add(new int[] { w, x });
        decr[x].Add(new int[] { -w, y });
        decr[y].Add(new int[] { -w, x });
    }
 
    // Function implementing the modified Dijkstra algorithm
    static int Modified_Dijkstra()
    {
        // Priority queue to store vertices
        var q = new PriorityQueue<PQPair>((a, b) => b.First[0] - a.First[0]);
 
        // Sort the edges
        for (int i = 1; i <= n; i++)
        {
            incr[i].Sort((a, b) => (a[0] != b[0]) ? a[0] - b[0] : a[1] - b[1]);
            decr[i].Sort((a, b) => (a[0] != b[0]) ? a[0] - b[0] : a[1] - b[1]);
        }
 
        // Initializing shortest distances
        for (int i = 1; i <= n; i++)
            shortest[i] = MAXI;
 
        // Push the source vertex
        q.Push(new PQPair(new int[] { 0, 0 }, new int[] { 0, src }));
 
        while (!q.IsEmpty())
        {
            // Remove the top element from the queue
            var FRONT = q.Pop();
 
            // Store all the values
            int cost = -FRONT.First[0];
            int stage = FRONT.First[1];
            int weight = FRONT.Second[0];
            int v = FRONT.Second[1];
 
            // Take the minimum cost for the vertex
            shortest[v] = Math.Min(shortest[v], cost);
 
            // If destination vertex has already been visited
            if (shortest[dest] != MAXI)
                break;
 
            // To make difference negative
            if (stage == 1)
            {
                // Start from last not visited vertex
                for (int i = _incr[v]; i < incr[v].Count; i++)
                {
                    // If we can take the ith vertex
                    if (weight > incr[v][i][0])
                        q.Push(new PQPair(
                            new int[] { -cost - incr[v][i][0], 0 },
                            new int[] { incr[v][i][0], incr[v][i][1] }
                        ));
                    else
                    {
                        // To keep the last not visited vertex
                        _incr[v] = i;
                        break;
                    }
                }
            }
 
            // To make difference positive
            else
            {
                // Start from last not visited vertex
                for (int i = _decr[v]; i < decr[v].Count; i++)
                {
                    // If we can take the ith vertex
                    if (weight < -decr[v][i][0])
                        q.Push(new PQPair(
                            new int[] { -cost + decr[v][i][0], 1 },
                            new int[] { -decr[v][i][0], decr[v][i][1] }
                        ));
                    else
                    {
                        // To keep the last not visited vertex
                        _decr[v] = i;
                        break;
                    }
                }
            }
        }
 
        if (shortest[dest] == MAXI)
            return -1;
 
        return shortest[dest];
    }
 
    // Main method
    static void Main(string[] args)
    {
        n = 5; src = 4; dest = 3;
 
        // Adding edges to the graph
        Add_edge(4, 2, 8);
        Add_edge(1, 4, 6);
        Add_edge(2, 3, 10);
        Add_edge(3, 1, 10);
        Add_edge(1, 2, 3);
        Add_edge(3, 5, 3);
 
        // Calling the modified Dijkstra algorithm and printing the result
        Console.WriteLine(Modified_Dijkstra());
    }
}

                    

Javascript

class PriorityQueue {
    constructor(comparator) {
        this.heap = [];
        this.comparator = comparator;
    }
 
    push(value) {
        this.heap.push(value);
        this.heap.sort(this.comparator);
    }
 
    pop() {
        return this.heap.shift();
    }
 
    isEmpty() {
        return this.heap.length === 0;
    }
}
 
 class PQPair {
    constructor(first, second) {
        this.first = first;
        this.second = second;
    }
}
 
function Add_edge(x, y, w) {
    incr[x].push([w, y]);
    incr[y].push([w, x]);
    decr[x].push([-w, y]);
    decr[y].push([-w, x]);
}
 
function Modified_Dijkstra() {
    // Total cost, stage, weight of previous, vertex
    const q = new PriorityQueue((a, b) => b.first[0] - a.first[0]);
 
    // Sort the edges
    for (let i = 1; i <= n; i++) {
        incr[i].sort((a, b) => (a[0] !== b[0]) ? a[0] - b[0] : a[1] - b[1]);
        decr[i].sort((a, b) => (a[0] !== b[0]) ? a[0] - b[0] : a[1] - b[1]);
    }
 
    for (let i = 1; i <= n; i++)
        shortest[i] = MAXI;
 
    // Push the source vertex
    q.push(new PQPair([0, 0], [0, src]));
 
    while (!q.isEmpty()) {
        // Remove the top element from the queue
        const FRONT = q.pop();
 
        // Store all the values
        const cost = -FRONT.first[0];
        const stage = FRONT.first[1];
        const weight = FRONT.second[0];
        const v = FRONT.second[1];
 
        // Take the minimum cost for the vertex
        shortest[v] = Math.min(shortest[v], cost);
 
        // If destination vertex has already been visited
        if (shortest[dest] !== MAXI)
            break;
 
        // To make difference negative
        if (stage === 1) {
            // Start from last not visited vertex
            for (let i = _incr[v]; i < incr[v].length; i++) {
                // If we can take the ith vertex
                if (weight > incr[v][i][0])
                    q.push(new PQPair(
                        [-cost - incr[v][i][0], 0],
                        [incr[v][i][0], incr[v][i][1]]
                    ));
                else {
                    // To keep the last not visited vertex
                    _incr[v] = i;
                    break;
                }
            }
        }
 
        // To make difference positive
        else {
            // Start from last not visited vertex
            for (let i = _decr[v]; i < decr[v].length; i++) {
                // If we can take the ith vertex
                if (weight < -decr[v][i][0])
                    q.push(new PQPair(
                        [-cost + decr[v][i][0], 1],
                        [-decr[v][i][0], decr[v][i][1]]
                    ));
                else {
                    // To keep the last not visited vertex
                    _decr[v] = i;
                    break;
                }
            }
        }
    }
 
    if (shortest[dest] === MAXI)
        return -1;
 
    return shortest[dest];
}
 
const n = 5;
const src = 4;
const dest = 3;
const incr = new Array(n + 1).fill(null).map(() => []);
const decr = new Array(n + 1).fill(null).map(() => []);
const _incr = new Array(n + 1).fill(0);
const _decr = new Array(n + 1).fill(0);
const shortest = new Array(n + 1);
const MAXI = 1 << 30;
 
// Adding edges
Add_edge(4, 2, 8);
Add_edge(1, 4, 6);
Add_edge(2, 3, 10);
Add_edge(3, 1, 10);
Add_edge(1, 2, 3);
Add_edge(3, 5, 3);
 
console.log(Modified_Dijkstra());

                    

Output
19

Time complexity:
The time complexity of the Modified Dijkstra’s algorithm is O((E+V)logV), where E is the number of edges and V is the number of vertices. In this implementation, we are using a priority queue to implement the algorithm, and sorting the edges takes O(ElogE) time. Therefore, the overall time complexity of this implementation is O((E+V)logV + ElogE).

Space complexity:
The space complexity of this implementation is O(E+V), where E is the number of edges and V is the number of vertices.



Last Updated : 28 Feb, 2024
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads