Number of Ways to Arrive at Destination
Problem
You are in a city that consists of n intersections numbered from 0 to n - 1 with bi-directional roads between some intersections. The inputs are generated such that you can reach any intersection from any other intersection and that there is at most one road between any two intersections.
You are given an integer n and a 2D integer array roads where roads[i] = [ui, vi, timei] means that there is a road between intersections ui and vi that takes timei minutes to travel. You want to know in how many ways you can travel from intersection 0 to intersection n - 1 in the shortest amount of time.
Return the number of ways you can arrive at your destination in the shortest amount of time. Since the answer may be large, return it modulo 10^9 + 7.
Examples
Example 1:
graph TD A(0) B(1) C(2) D(3) E(4) F(5) G(6) A ---|5| E A ---|7| G A ---|2| B B ---|3| C B ---|3| D G ---|3| D D ---|1| F G ---|1| F C ---|1| F E ---|2| G
Input: n = 7, roads = [[0,6,7],[0,1,2],[1,2,3],[1,3,3],[6,3,3],[3,5,1],[6,5,1],[2,5,1],[0,4,5],[4,6,2]]
Output: 4
Explanation: The shortest amount of time it takes to go from intersection 0 to intersection 6 is 7 minutes.
The four ways to get there in 7 minutes are:
- 0 ➝ 6
- 0 ➝ 4 ➝ 6
- 0 ➝ 1 ➝ 2 ➝ 5 ➝ 6
- 0 ➝ 1 ➝ 3 ➝ 5 ➝ 6
Example 2:
Input: n = 2, roads = [[1,0,10]]
Output: 1
Explanation: There is only one way to go from intersection 0 to intersection 1, and it takes 10 minutes.
Constraints:
1 <= n <= 200n - 1 <= roads.length <= n * (n - 1) / 2roads[i].length == 30 <= ui, vi <= n - 11 <= timei <= 10^9ui != vi- There is at most one road connecting any two intersections.
- You can reach any intersection from any other intersection.
Solution
Method 1 - Using Dijkstra Algorithm
To solve this problem, we aim to compute the number of ways to reach intersection n-1 from 0 using the shortest time possible. This involves using Dijkstra's algorithm to find the shortest path and counting the number of paths that result in the shortest time.
Approach
- Graph Representation: Represent the city as an adjacency list where each node points to its neighbours with the road times.
- Shortest Path Calculation:
Implement Dijkstra's algorithm to calculate the shortest path from intersection
0ton-1. - Counting Paths:
During the execution of Dijkstra's algorithm, maintain two arrays:
dist: Stores the shortest distance from0to each node.ways: Tracks how many ways we can reach each node using the shortest time.
- Modulo Constraint:
As the problem requires, return the count modulo
10^9 + 7.
Code
Java
class Solution {
public int countPaths(int n, int[][] roads) {
List<List<int[]>> graph = new ArrayList<>();
for (int i = 0; i < n; i++) {
graph.add(new ArrayList<>());
}
for (int[] road : roads) {
int u = road[0], v = road[1], w = road[2];
graph.get(u).add(new int[]{v, w});
graph.get(v).add(new int[]{u, w});
}
// Dijkstra's algorithm components
PriorityQueue<long[]> pq = new PriorityQueue<>((a, b) -> Long.compare(a[0], b[0])); // Min-heap
pq.offer(new long[]{0, 0}); // {distance, node}
long[] dist = new long[n];
Arrays.fill(dist, Long.MAX_VALUE);
dist[0] = 0;
int[] ways = new int[n];
ways[0] = 1;
final int MOD = 1_000_000_007;
while (!pq.isEmpty()) {
long[] curr = pq.poll();
long time = curr[0];
int node = (int) curr[1];
if (time > dist[node]) {
continue;
}
for (int[] nei : graph.get(node)) {
int nextNode = nei[0], travelTime = nei[1];
long newTime = time + travelTime;
// Shorter path found
if (newTime < dist[nextNode]) {
dist[nextNode] = newTime;
pq.offer(new long[]{newTime, nextNode});
ways[nextNode] = ways[node];
}
// Equal path length found -> Accumulate ways
else if (newTime == dist[nextNode]) {
ways[nextNode] = (ways[nextNode] + ways[node]) % MOD;
}
}
}
return ways[n - 1];
}
}
Python
class Solution:
def countPaths(self, n: int, roads: List[List[int]]) -> int:
MOD = 10**9 + 7
# Build graph as adjacency list
graph = [[] for _ in range(n)]
for u, v, time in roads:
graph[u].append((v, time))
graph[v].append((u, time))
# Initialise distances and ways arrays
dist = [float('inf')] * n
ways = [0] * n
dist[0] = 0
ways[0] = 1
# Priority queue for Dijkstra's algorithm
pq = [(0, 0)] # (distance, node)
while pq:
time, node = heapq.heappop(pq)
# Skip outdated distance records
if time > dist[node]:
continue
# Process neighbours
for neighbor, travel_time in graph[node]:
new_time = time + travel_time
# Found a shorter path
if new_time < dist[neighbor]:
dist[neighbor] = new_time # Update the shortest distance
ways[neighbor] = ways[node] # Inherit the number of ways
heapq.heappush(pq, (new_time, neighbor))
# Found an alternate path with the same shortest distance
elif new_time == dist[neighbor]:
ways[neighbor] = (ways[neighbor] + ways[node]) % MOD # Accumulate the number of ways
# Return number of ways to reach the last node
return ways[n - 1]
Complexity
- ⏰ Time complexity:
O((n + roads.size()) * log(n)).- Graph Construction:
O(n + roads.size()), whereroads.size()is the number of roads. - Dijkstra's Algorithm:
O((n + roads.size()) * log(n))using a priority queue.
- Graph Construction:
- 🧺 Space complexity:
O(n + roads.size())- Graph Storage:
O(n + roads.size()). - Extra Arrays (
distandways):O(n).
- Graph Storage: