Open In App

Queries on Graph for finding shortest path with maximum coins

Improve
Improve
Like Article
Like
Save
Share
Report

Given an unweighted directed graph with N vertices and M edges and array A[] of size N. Given Q queries of the form (U, V), the task for this problem is for every query to print the shortest path from U to V and print the maximum coins collected for that shortest path. For visiting, Node ‘i’ collects A[i] coins. If traveling from U to V is not possible print -1.

Examples:

Input: N = 5, Edg[] = {{1, 2}, {1, 3}, {2, 3}, {3, 4}, {3, 5}, {4, 1}, {5, 1}}, A[] = {30, 50, 70, 20, 60}, Q[] = {{1, 3}, {3, 1}, {4, 5}};
Output: 1 100
2 160
3 180
Explanation: 

  • Length of Shortest path from 1 to 3 is 1 (1 -> 3) and coins collected = coins at node1 + coins at node3 = 30 + 70 = 100
  • Length of Shortest path from 3 to 1 is 2 (3 -> 5 -> 1) and coins collected = coins at node3 + coins at node5 + coins at node1 = 70 + 60 + 30 = 160
  • Length of Shortest path from 4 to 5 is 3 (4 -> 1 -> 3 -> 5) and coins collected = coins at node4 + coins at node1 + coins at node3 + coins at node5 = 20 + 30 + 70 + 60 = 180 

Input: N2 = 2, Edg[] = {}, A[] = {100, 100}, Q[] = {{1, 2}}
Output:  -1

Naive approach: The basic way to solve the problem is as follows:

The basic way to solve this problem is to generate all possible combinations by using a recursive approach.

Time Complexity: O(N!)
Auxiliary Space: O(1)

Efficient Approach:  The above approach can be optimized based on the following idea:

Dynamic programming can be used to solve this problem

dp[i][j] represents shortest path from i to j.

Follow the steps below to solve the problem:

  • Create dp[N][N] and ANS[N][N] tables with all values set to INT_MAX and INT_MIN.
  • Iterate over all M edges and for each edge U and V set dp[U][V] to 1 and ANS[U][V] to A[U] + A[V].
  • Fill the dp[N][N] table by iterating on all states.
  • Iterate each query and print the answer

Below is the implementation of the above approach:

C++




// C++ code for the above approach:
#include <bits/stdc++.h>
using namespace std;
 
// To avoid overflow
#define int long long
 
// Function for finding shortest paths
// with maximized coins
void shortPath(int N, vector<vector<int> >& edg,
               vector<int>& A, vector<vector<int> >& Q)
{
 
    // Initializing dp table for
    // finding shortest path
    vector<vector<int> > dp(N + 1,
                            vector<int>(N + 1, INT_MAX));
 
    // Initializing another table
    // to store max coins
    vector<vector<int> > ans(N + 1,
                             vector<int>(N + 1, INT_MIN));
 
    // Iterating over all M edges
    for (int i = 0; i < edg.size(); i++) {
 
        // There is edge exists from
        // edg[i][0] to edg[i][1]
        dp[edg[i][0]][edg[i][1]] = 1;
 
        // Answer variable stores
        ans[edg[i][0]][edg[i][1]]
            = A[edg[i][0] - 1] + A[edg[i][1] - 1];
    }
 
    // Inserting from 1 to N
    for (int k = 1; k <= N; k++) {
 
        // Iterating on all 1 to N
        for (int i = 1; i <= N; i++) {
 
            // Iterating on all 1 to N
            for (int j = 1; j <= N; j++) {
 
                // If path through k is
                // shortest then relax
                // the weight
                if (dp[i][j] > dp[i][k] + dp[k][j]) {
                    dp[i][j] = dp[i][k] + dp[k][j];
                    ans[i][j]
                        = ans[i][k] + ans[k][j] - A[k - 1];
                }
 
                // Maximise coins
                else if (dp[i][j] == dp[i][k] + dp[k][j]
                         and ans[i][j] < ans[i][k]
                                             + ans[k][j]
                                             - A[k - 1]) {
                    ans[i][j]
                        = ans[i][k] + ans[k][j] - A[k - 1];
                }
            }
        }
    }
 
    // Iterating for all queries
    for (int i = 0; i < Q.size(); i++) {
 
        // Query
        int U = Q[i][0], V = Q[i][1];
 
        // Printing answer for query
        // if it exists
        if (dp[U][V] <= N * N)
            cout << dp[U][V] << " " << ans[U][V] << endl;
 
        // If no path exists
        else
            cout << "-1" << endl;
    }
}
 
// Driver program
int32_t main()
{
 
    // Test case 1
    int N1 = 5;
    vector<vector<int> > edg1
        = { { 1, 2 }, { 1, 3 }, { 2, 3 }, { 3, 4 }, { 3, 5 }, { 4, 1 }, { 5, 1 } };
    vector<int> A1 = { 30, 50, 70, 20, 60 };
    vector<vector<int> > Q1
        = { { 1, 3 }, { 3, 1 }, { 4, 5 } };
 
    // Call for test case 1
    shortPath(N1, edg1, A1, Q1);
 
    cout << endl;
 
    // Test case 2
    int N2 = 2;
    vector<vector<int> > edg2 = {};
    vector<int> A2 = { 100, 100 };
    vector<vector<int> > Q2 = { { 1, 2 } };
 
    // Call for test case 2
    shortPath(N2, edg2, A2, Q2);
    return 0;
}


Java




//Java code for the above approach
import java.util.Arrays;
 
class GFG {
    static void shortPath(int N, int[][] edg, int[] A, int[][] Q) {
        // Initializing dp table for finding shortest path
        int[][] dp = new int[N + 1][N + 1];
        int[][] ans = new int[N + 1][N + 1];
 
        for (int i = 0; i < edg.length; i++) {
            dp[edg[i][0]][edg[i][1]] = 1;
            ans[edg[i][0]][edg[i][1]] = A[edg[i][0] - 1] + A[edg[i][1] - 1];
        }
 
        // Initializing another table to store max coins
        for (int k = 1; k <= N; k++) {
            for (int i = 1; i <= N; i++) {
                for (int j = 1; j <= N; j++) {
                    if (dp[i][k] > 0 && dp[k][j] > 0) {
                        // check if i and k, and k and j are
                        // connected
                        if (dp[i][j] == 0 || dp[i][j] > dp[i][k] + dp[k][j]) {
                            dp[i][j] = dp[i][k] + dp[k][j];
                            ans[i][j] = ans[i][k] + ans[k][j] - A[k - 1];
                        } else if (dp[i][j] == dp[i][k] + dp[k][j] && ans[i][j] < ans[i][k] + ans[k][j] - A[k - 1]) {
                            ans[i][j] = ans[i][k] + ans[k][j] - A[k - 1];
                        }
                    }
                }
            }
        }
 
         
        for (int i = 0; i < Q.length; i++) {
             
            // Printing answer for query
            // if it exists
            int U = Q[i][0];
            int V = Q[i][1];
            if (dp[U][V] > 0 && dp[U][V] <= N * N) {
                System.out.println(dp[U][V] + " " + ans[U][V]);
            } else {
                // If no path exists
                System.out.println("-1");
            }
        }
    }
 
    // Driver program
    public static void main(String[] args) {
        // Test case 1
        int N1 = 5;
        int[][] edg1 = {
                {1, 2}, {1, 3},
                {2, 3}, {3, 4},
                {3, 5}, {4, 1},
                {5, 1}
        };
        int[] A1 = {30, 50, 70, 20, 60};
        int[][] Q1 = {
                {1, 3},
                {3, 1},
                {4, 5}
        };
        // Call for test case 1
        shortPath(N1, edg1, A1, Q1);
        System.out.println();
 
        // Test case 2
        int N2 = 2;
        int[][] edg2 = {};
        int[] A2 = {100, 100};
        int[][] Q2 = {{1, 2}};
        // Call for test case 2
        shortPath(N2, edg2, A2, Q2);
        System.out.println();
    }
}
 
// This code is contributed by Pushpesh Raj.


Python3




#Python code for the above approach:
import sys
 
# To avoid overflow
sys.setrecursionlimit(10**7)
 
def shortPath(N, edg, A, Q):
 
    # Initializing dp table for
    # finding shortest path
    dp = [[float("inf") for j in range(N + 1)] for i in range(N + 1)]
 
    # Initializing another table
    # to store max coins
    ans = [[-sys.maxsize for j in range(N + 1)] for i in range(N + 1)]
 
    # Iterating over all M edges
    for i in range(len(edg)):
 
        # There is edge exists from
        # edg[i][0] to edg[i][1]
        dp[edg[i][0]][edg[i][1]] = 1
 
        # Answer variable stores
        ans[edg[i][0]][edg[i][1]] = A[edg[i][0] - 1] + A[edg[i][1] - 1]
 
    # Inserting from 1 to N
    for k in range(1, N+1):
 
        # Iterating on all 1 to N
        for i in range(1, N+1):
 
            # Iterating on all 1 to N
            for j in range(1, N+1):
 
                # If path through k is
                # shortest then relax
                # the weight
                if dp[i][j] > dp[i][k] + dp[k][j]:
                    dp[i][j] = dp[i][k] + dp[k][j]
                    ans[i][j] = ans[i][k] + ans[k][j] - A[k - 1]
 
                # Maximise coins
                elif (dp[i][j] == dp[i][k] + dp[k][j] and
                      ans[i][j] < ans[i][k] + ans[k][j] - A[k - 1]):
                    ans[i][j] = ans[i][k] + ans[k][j] - A[k - 1]
 
    # Iterating for all queries
    for i in range(len(Q)):
 
        # Query
        U = Q[i][0]
        V = Q[i][1]
 
        # Printing answer for query
        # if it exists
        if dp[U][V] <= N * N:
            print(dp[U][V], ans[U][V])
 
        # If no path exists
        else:
            print("-1")
 
# Test case 1
N1 = 5
edg1 = [[1, 2], [1, 3], [2, 3], [3, 4], [3, 5], [4, 1], [5, 1]]
A1 = [30, 50, 70, 20, 60]
Q1 = [[1, 3], [3, 1], [4, 5]]
 
# Call for test case 1
shortPath(N1, edg1, A1, Q1)
 
print()
 
# Test case 2
N2 = 2
edg2 = []
A2 = [100, 100]
Q2 = [[1, 2]]
 
#Call for test case 2
shortPath(N2, edg2, A2, Q2)
print()
 
#This code is contributed by shivamsharma215


C#




// C# code for the above approach:
using System;
 
class Program {
    static void shortPath(int N, int[][] edg, int[] A,
                          int[][] Q)
    {
        // Initializing dp table for finding shortest path
        var dp = new int[N + 1, N + 1];
        var ans = new int[N + 1, N + 1];
 
        for (var i = 0; i < edg.Length; i++) {
            dp[edg[i][0], edg[i][1]] = 1;
            ans[edg[i][0], edg[i][1]]
                = A[edg[i][0] - 1] + A[edg[i][1] - 1];
        }
        // Initializing another table to store max coins
        for (var k = 1; k <= N; k++) {
            for (var i = 1; i <= N; i++) {
                for (var j = 1; j <= N; j++) {
                    if (dp[i, k] > 0 && dp[k, j] > 0) {
 
                        // check if i and k, and k and j are
                        // connected
                        if (dp[i, j] == 0
                            || dp[i, j]
                                   > dp[i, k] + dp[k, j]) {
                            dp[i, j] = dp[i, k] + dp[k, j];
                            ans[i, j] = ans[i, k]
                                        + ans[k, j]
                                        - A[k - 1];
                        }
                        else if (dp[i, j]
                                     == dp[i, k] + dp[k, j]
                                 && ans[i, j]
                                        < ans[i, k]
                                              + ans[k, j]
                                              - A[k - 1]) {
                            ans[i, j] = ans[i, k]
                                        + ans[k, j]
                                        - A[k - 1];
                        }
                    }
                }
            }
        }
        for (var i = 0; i < Q.Length; i++) {
 
            // Printing answer for query
            // if it exists
            var U = Q[i][0];
            var V = Q[i][1];
            if (dp[U, V] > 0 && dp[U, V] <= N * N) {
                Console.WriteLine(dp[U, V] + " "
                                  + ans[U, V]);
            }
            else {
 
                // If no path exists
                Console.WriteLine("-1");
            }
        }
    }
    // Driver program
 
    static void Main()
    {
        // Test case 1
        var N1 = 5;
        var edg1 = new int[][] {
            new int[] { 1, 2 }, new int[] { 1, 3 },
            new int[] { 2, 3 }, new int[] { 3, 4 },
            new int[] { 3, 5 }, new int[] { 4, 1 },
            new int[] { 5, 1 }
        };
        var A1 = new int[] { 30, 50, 70, 20, 60 };
        var Q1 = new int[][] { new int[] { 1, 3 },
                               new int[] { 3, 1 },
                               new int[] { 4, 5 } };
        shortPath(N1, edg1, A1, Q1);
        // Call for test case 1
        Console.WriteLine();
 
        // Test case 2
        var N2 = 2;
        var edg2 = new int[0][];
        var A2 = new int[] { 100, 100 };
        var Q2 = new int[][] { new int[] { 1, 2 } };
        // Call for test case 2
        shortPath(N2, edg2, A2, Q2);
        Console.WriteLine();
    }
}


Javascript




// JavaScript code for the above approach:
 
// Function for finding shortest paths
// with maximized coins
function shortPath(N, edg, A, Q) {
     
    // Initializing dp table for
    // finding shortest path
    var dp = Array.from({length: N+1}, () => Array(N+1).fill(Number.POSITIVE_INFINITY));
     
    // Initializing another table
    // to store max coins
    var ans = Array.from({length: N+1}, () => Array(N+1).fill(-Infinity));
     
    // Iterating over all M edges
    for (var i = 0; i < edg.length; i++) {
         
        // There is edge exists from
        // edg[i][0] to edg[i][1]
        dp[edg[i][0]][edg[i][1]] = 1;
         
        // Answer variable stores
        ans[edg[i][0]][edg[i][1]] = A[edg[i][0] - 1] + A[edg[i][1] - 1];
    }
     
    // Inserting from 1 to N
    for (var k = 1; k <= N; k++) {
         
        // Iterating on all 1 to N
        for (var i = 1; i <= N; i++) {
             
            // Iterating on all 1 to N
            for (var j = 1; j <= N; j++) {
                 
                // If path through k is
                // shortest then relax
                // the weight
                if (dp[i][j] > dp[i][k] + dp[k][j]) {
                    dp[i][j] = dp[i][k] + dp[k][j];
                    ans[i][j] = ans[i][k] + ans[k][j] - A[k - 1];
                }
                 
                // Maximise coins
                else if (dp[i][j] == dp[i][k] + dp[k][j] && ans[i][j] <
                    ans[i][k] + ans[k][j] - A[k - 1]) {
                    ans[i][j] = ans[i][k] + ans[k][j] - A[k - 1];
                }
            }
        }
    }
     
    // Iterating for all queries
    for (var i = 0; i < Q.length; i++) {
         
        // Query
        var U = Q[i][0];
        var V = Q[i][1];
         
        // Printing answer for query
        // if it exists
        if (dp[U][V] <= N * N) {
            console.log(dp[U][V], ans[U][V]);
        }
         
        // If no path exists
        else {
            console.log("-1");
        }
    }
}
 
// Driver program
 
// Test case 1
var N1 = 5;
var edg1 = [[1, 2], [1, 3], [2, 3], [3, 4], [3, 5], [4, 1], [5, 1]];
var A1 = [30, 50, 70, 20, 60];
var Q1 = [[1, 3], [3, 1], [4, 5]];
 
// Call for test case 1
shortPath(N1, edg1, A1, Q1);
 
console.log();
 
// Test case 2
var N2 = 2;
var edg2 = [];
var A2 = [100, 100];
var Q2 = [[1, 2]];
 
// Call for test case 2
shortPath(N2, edg2, A2, Q2);
console.log();
 
// This code is contributed by Prasad Kandekar(prasad264)


Output

1 100
2 160
3 180

-1

Time Complexity: O(N3)  
Auxiliary Space: O(N2)

Related Articles:



Last Updated : 27 Jul, 2023
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads