Given list of cities and the time taken to reach from a city to a city by
q. Road
B. By flight
There may be some city to city which has either road or flight or both
Find the shortest path from source to destination subject to having only one flight almost
Example: A,B,C,D,E cities
A -> Road -> B = 30
A -> Flight -> B = 20
B -> Road -> D = 30
B -> Road -> E = 100
B -> Flight -> E = 40
B -> Road -> C = 20
C -> Road -> E = 5
D -> Road -> E = 5
A -> Flight -> D = 4
D -> flight -> E = 2
Source: A , Destination: E
Paths
A -> Road -> B -> Road -> D -> Road -> E = 30 + 30 + 5 = 65
A -> Road -> B -> Road -> E = 30 + 100 = 130
A -> Road -> B -> Flight -> E = 30 + 40 = 70
A -> Road -> B -> Road -> C -> Road -> E = 30+20+5 = 65
A -> Flight -> B -> Road -> D -> Road -> E = 20 + 30 + 5 = 55
A -> Flight -> B -> Road -> E = 20 + 100 = 120
A -> Flight -> B -> Flight -> E = 20 + 40 = 60 { discard, > 1 flight }
A -> Flight -> B -> Road -> C -> Road -> E = 20+20+5 = 45
A -> Flight -> D -> Flight -> E = 4+2=6 { discard, > 1 flight }
.....
.....
Shortest route is with at most 1 flight is
A -> Flight -> B -> Road -> C -> Road -> E = 20+20+5 = 45
Input: a matrix of nn where n is number of cities. Each cell of matrix represent two values (r,f)
if there is a route from A -> B vai rode then r > -1 and vai flight then f > -1 otherwise they will be -1 if no route is there
cityMap = [
[
[( -1 , -1 ) ,( 30 , 20 ) ,( -1 , -1 ) ,( -1 , -1 ) ,( -1 , -1 ) ]
[( -1 , -1 ) ,( -1 , -1 ) ,( 20 , -1 ) ,( 30 , -1 ) ,( 100 , 40 ) ]
[( -1 , -1 ) ,( -1 , -1 ) ,( -1 , -1 ) ,( -1 , -1 ) ,( 5 , -1 ) ]
[( -1 , -1 ) ,( -1 , -1 ) ,( -1 , -1 ) ,( -1 , -1 ) ,( 5 , 2 ) ]
[( -1 , -1 ) ,( -1 , -1 ) ,( -1 , -1 ) ,( -1 , -1 ) ,( -1 , -1 ) ]
] flightCount 1
]
expected: 45
path :A->(R:30)B->(R:20)C->(R:5)E road cost :55 flight cost :0
path :A->(R:30)B->(R:30)D->(R:5)E road cost :65 flight cost :0
path :A->(R:30)B->(R:30)D->(F:2)E road cost :60 flight cost :2
path :A->(R:30)B->(R:100)E road cost :130 flight cost :0
path :A->(R:30)B->(F:40)E road cost :30 flight cost :40
path :A->(F:20)B->(R:20)C->(R:5)E road cost :25 flight cost :20
path :A->(F:20)B->(R:30)D->(R:5)E road cost :35 flight cost :20
path :A->(F:20)B->(R:100)E road cost :100 flight cost :20
Best Path A->(F:20)B->(R:20)C->(R:5)E
Output :45
I've tried to solve this using backtracking {code is attached}. But i believe we can use dynamic programming since there are many overlapping problems are there.
I tried hard to form the releation but not succeed 100% yet.
My initial thought: I believe we need to cache the cost of point 'i' to point 'j' with count of flight 'flightCount'
Hence dp[i][j][flightCount] must represet minimum cost to reach from i->j with flight -> flightcount
Any help ?
Backtracking code:
final Map<Integer, Character> map = new HashMap<>();
public int shortestPathCost(final CitiCell[][] cityMap, final int sourceX, final int destinationX, final int flightsCount) {
if (cityMap == null || cityMap.length == 0 || cityMap[0].length == 0)
return 0;
String[] path = "";
int[] cost = {1000};
for (int i = 0; i < cityMap.length; i++) {
map.put(i, (char) (i + 65));
}
StringBuilder temp = new StringBuilder();
temp.append(map.get(sourceX));
shortestPathCost(cityMap, sourceX, destinationX, flightsCount, temp, path, cost, 0, 0);
System.out.println("Best Path " + path[0]);
return cost[0];
}
private void shortestPathCost(CitiCell[][] cityMap, int sourceX, int destinationX, int flightsCount, StringBuilder currentPath, String[] bestPath, int[] cost, int roadCost, int flightCost) {
//2. Our constraint: a) we can only take a path if exist either vai road or flight . b) we can use at most number of given flight
if (flightsCount < 0)
return;
//1. Our goal : to reach destination. Whenever we reach destination, we'll record least cost and best path
if (sourceX == destinationX) {
System.out.println("\npath :" + currentPath + " road cost :" + roadCost + " flight cost :" + flightCost);
if (cost[0] > flightCost + roadCost) {
cost[0] = flightCost + roadCost;
bestPath[0] = currentPath.toString();
}
return;
}
//3. Our choices: we can try all the path that is connected either by road or flight or both.
for (int i = 0; i < cityMap.length; i++) {
CitiCell current = cityMap[sourceX][i];
if (i == sourceX || (current.r == -1 && current.f == -1))
continue;
int length = currentPath.length();
//By road
if (current.r != -1) {
currentPath.append("->(R:").append(current.r).append(")").append(map.get(i));
shortestPathCost(cityMap, i, destinationX, flightsCount, currentPath, bestPath, cost, roadCost + current.r, flightCost);
currentPath.setLength(length);
}
//By flight
if (current.f != -1) {
currentPath.append("->(F:").append(current.f).append(")").append(map.get(i));
shortestPathCost(cityMap, i, destinationX, flightsCount - 1, currentPath, bestPath, cost, roadCost, flightCost + current.f);
currentPath.setLength(length);
}
}
}Here is the solution using Dijkstra Shortest Path LIKE Algorithm
Thanks @MikeBonzai
static class Distance {
int withoutFlight, withFlight;
public Distance() {
this.withoutFlight = Integer.MAX_VALUE;
this.withFlight = Integer.MAX_VALUE;
}
@Override
public String toString() {
return "Distance{" +
"withoutFlight=" + withoutFlight +
", withFlight=" + withFlight +
'}';
}
}
static class Node {
int distance;
int node;
int flightCount;
public Node(int distance, int node, int isFlight) {
this.distance = distance;
this.node = node;
this.flightCount = isFlight;
}
@Override
public String toString() {
return "Node{" +
"distance=" + distance +
", node=" + node +
", flightCount=" + flightCount +
'}';
}
}
public int shortestPathCost(final CityCell[][] cityMap, final int sourceX, final int destinationX, final int flightsCount) {
if (cityMap == null || cityMap.length == 0 || cityMap[0].length == 0)
return 0;
final Queue<Node> queue = new LinkedList<>();
final Distance[] distance = new Distance[cityMap.length];
for (int i = 0; i < cityMap.length; i++)
distance[i] = new Distance();
//Offer the start node in the queue with 0 distance and no flight
queue.offer(new Node(0, sourceX, 0));
distance[sourceX].withFlight = 0;
distance[sourceX].withoutFlight = 0;
//Process all paths
while (!queue.isEmpty()) {
//pull first path
Node current = queue.poll();
// all neighbours
CityCell[] connectedCities = cityMap[current.node];
for (int i = 0; i < connectedCities.length; i++) {
CityCell cityCell = connectedCities[i];
if (i == current.node || (cityCell.f == -1 && cityCell.r == -1))
continue;
//See if this neighbour is gives minimum distance by road
if (cityCell.r != -1 && distance[i].withoutFlight >= current.distance + cityCell.r) {
distance[i].withoutFlight = current.distance + cityCell.r;
queue.offer(new Node(distance[i].withoutFlight, i, current.flightCount));
}
//See if this neighbour is gives minimum distance by flight and we have 1 more flight left from current location so far
if (cityCell.f != -1 && current.flightCount + 1 <= flightsCount && distance[i].withFlight >= current.distance + cityCell.f) {
distance[i].withFlight = current.distance + cityCell.f;
queue.offer(new Node(distance[i].withFlight, i, current.flightCount + 1));
}
}
}
//get minimum distance with/without flight
return Math.min(distance[destinationX].withFlight, distance[destinationX].withoutFlight);
}