Shortest path in a graph from a source S to destination D with exactly K edges for multiple Queries


Finding the shortest route between a source vertex and a target vertex in a graph with precisely K edges is one of the most typical graph traversal issues. The objective is to find the shortest path with minimum weight and exactly K edges. This issue can manifest in a number of practical contexts, including transportation networks, routing protocols, and resource allocation.

Dynamic Programming (DP), and Dijkstra's Algorithm are just some of the strategies that may be used to attack this issue. The shortest path under the given constraints can be found using one of many methods. Dijkstra's Algorithm takes into account the weight of each edge to find the shortest path. DP uses overlapping subproblems to derive optimum solutions.

Methods Used

  • Dynamic Programming

  • Dijkstra's algorithm

Dynamic Programming

When applying the Dynamic Programming technique, the problem at hand is first broken down into a series of simpler, overlapping subproblems. DP may be used to quickly determine the shortest path for every possible combination of vertex, edge count, and weight in the context of finding the shortest path with exactly K edges. DP delivers an optimised answer by avoiding unnecessary calculations and storing the ideal solutions in a 2D array. When the issue has an optimum substructure, the procedure can be used again with the same inputs to get the same answer.

Algorithm

  • Create a N x (K+1) two−dimensional array dp with all of its values set to infinity, then provide it to the function shortestPathDP(graph, source, destination, K).

  • For all k between 1 and K, for all vertices v between 0 and N−1, for all neighbours in graph[v], set dp[source][0] = 0.

  • Whenever dp[neighbor.vertex][k−1] = 0:

  • The minimal value should be entered into dp[v][k].

Example

#include <iostream>
#include <vector>
#include <queue>
#include <climits>

using namespace std;

// Node structure
struct Node {
    int vertex;
    int weight;
    int edges;

    Node(int v, int w, int e) : vertex(v), weight(w), edges(e) {}
};


// DP method to find shortest path with exactly K edges
int shortestPathDP(vector<vector<Node>>& graph, int source, int destination, int K) {
    int N = graph.size();
    vector<vector<int>> dp(N, vector<int>(K + 1, INT_MAX));

    dp[source][0] = 0;

    for (int k = 1; k <= K; ++k) {
        for (int v = 0; v < N; ++v) {
            for (const auto& neighbor : graph[v]) {
                if (dp[neighbor.vertex][k - 1] != INT_MAX) {
                    dp[v][k] = min(dp[v][k], dp[neighbor.vertex][k - 1] + neighbor.weight);
                }
            }
        }
    }

    return dp[destination][K] != INT_MAX ? dp[destination][K] : -1;  // No path found
}

int main() {
    // Graph with default input
    int N = 5;  // Number of vertices
    vector<vector<Node>> graph(N);

    // Adding edges and weights
    graph[0].push_back(Node(1, 4, 1));
    graph[0].push_back(Node(2, 2, 1));
    graph[1].push_back(Node(2, 1, 1));
    graph[1].push_back(Node(3, 5, 1));
    graph[2].push_back(Node(3, 8, 1));
    graph[2].push_back(Node(4, 10, 1));
    graph[3].push_back(Node(4, 2, 1));

    int Q = 2;  // Number of queries

    // Queries
    vector<pair<int, pair<int, int> >> queries;
    queries.push_back(make_pair(0, make_pair(4, 1)));  // Source: 0, Destination: 4, K: 1
    queries.push_back(make_pair(1, make_pair(3, 2)));  // Source: 1, Destination: 3, K: 2

    for (int q = 0; q < Q; ++q) {
        int source = queries[q].first;
        int destination = queries[q].second.first;
        int K = queries[q].second.second;

        int shortestPathDPResult = shortestPathDP(graph, source, destination, K);

        cout << "Shortest path from " << source << " to " << destination << " with exactly " << K << " edges:\n";
      
        cout << "Using DP: " << shortestPathDPResult << endl;
        
    }

    return 0;
}

Output

Shortest path from 0 to 4 with exactly 1 edges:
Using DP: -1
Shortest path from 1 to 3 with exactly 2 edges:
Using DP: -1

Dijkstra’s Algorithm

The well−known graph traversal method known as the Dijkstra algorithm finds the shortest path between each vertex in the network by using weighted graphs. In order to pick the vertex with the shortest tentative distance at each stage, it keeps a priority queue.For graphs with non−negative edge weights, this approach is effective, and its findings are reliable in a variety of contexts.

Algorithm

  • Construct a two−dimensional array dist of size N by (K+1) and set its initial contents to infinity.

  • Make a new priority queue (pq) that is empty.

  • Put a pair into pq with no edge count, no vertex source, and no weight.

Example

#include <iostream>
#include <vector>
#include <queue>
#include <climits>

using namespace std;

// Node structure
struct Node {
    int vertex;
    int weight;
    int edges;

    Node(int v, int w, int e) : vertex(v), weight(w), edges(e) {}
};

// Dijkstra's algorithm to find shortest path with exactly K edges
int shortestPathDijkstra(vector<vector<Node>>& graph, int source, int destination, int K) {
    int N = graph.size();
    vector<vector<int>> dist(N, vector<int>(K + 1, INT_MAX));
    dist[source][0] = 0;

    priority_queue<pair<int, pair<int, int>>, vector<pair<int, pair<int, int>>>, greater<pair<int, pair<int, int>>>> pq;
    pq.push(make_pair(0, make_pair(source, 0)));

    while (!pq.empty()) {
        pair<int, pair<int, int>> curr = pq.top();
        pq.pop();

        int vertex = curr.second.first;
        int weight = curr.first;
        int edges = curr.second.second;

        if (vertex == destination && edges == K) {
            return weight;
        }

        if (weight > dist[vertex][edges]) {
            continue;
        }

        for (const auto& neighbor : graph[vertex]) {
            int newWeight = weight + neighbor.weight;
            int newEdges = edges + 1;

            if (newEdges <= K && newWeight < dist[neighbor.vertex][newEdges]) {
                dist[neighbor.vertex][newEdges] = newWeight;
                pq.push(make_pair(newWeight, make_pair(neighbor.vertex, newEdges)));
            }
        }
    }

    return -1;  // No path found
}

int main() {
    // Graph with default input
    int N = 5;  // Number of vertices
    vector<vector<Node>> graph(N);

    // Adding edges and weights
    graph[0].push_back(Node(1, 4, 1));
    graph[0].push_back(Node(2, 2, 1));
    graph[1].push_back(Node(2, 1, 1));
    graph[1].push_back(Node(3, 5, 1));
    graph[2].push_back(Node(3, 8, 1));
    graph[2].push_back(Node(4, 10, 1));
    graph[3].push_back(Node(4, 2, 1));

    int Q = 2;  // Number of queries

    // Queries
    vector<pair<int, pair<int, int>>> queries;
    queries.push_back(make_pair(0, make_pair(4, 1)));  // Source: 0, Destination: 4, K: 1
    queries.push_back(make_pair(1, make_pair(3, 2)));  // Source: 1, Destination: 3, K: 2

    for (int q = 0; q < Q; ++q) {
        int source = queries[q].first;
        int destination = queries[q].second.first;
        int K = queries[q].second.second;

        int shortestPathDijkstraResult = shortestPathDijkstra(graph, source, destination, K);

        cout << "Shortest path from " << source << " to " << destination << " with exactly " << K << " edges:\n";
        cout << "Using Dijkstra: " << shortestPathDijkstraResult << endl;
    }

    return 0;
}

Output

Shortest path from 0 to 4 with exactly 1 edges:
Using Dijkstra: -1
Shortest path from 1 to 3 with exactly 2 edges:
Using Dijkstra: 9

Conclusion

In graph theory, finding the shortest route between a given source and a specified destination in a network with exactly K edges is a challenging problem with several practical applications. Dynamic Programming (DP), and Dijkstra's Algorithm are just a few of the methods that may be used to effectively solve this problem and find the requisite shortest pathways.These techniques allow us to effectively manage many inquiries, locate optimal pathways within given restrictions, and promote efficient route planning and optimisation. These algorithms provide adaptability in issue solution, meeting the needs of a wide variety of graph architectures and problem specifications. In sum, these methods are helpful resources for tackling the shortest path issue in graphs with a fixed set of K edges.

Updated on: 17-Jul-2023

197 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements