Number of Ways to Reach Destination in Shortest Time - Graph Problem
Problem and examples
You are in a city with n intersections (numbered from 0 to n - 1) connected by bidirectional roads. Each road connects two intersections u and v and takes time minutes to travel.
You are given:
- An integer
n— the number of intersections - A 2D array
roadswhere each entry is[u, v, time]
Your goal is to determine how many different ways you can travel from intersection 0 (source) to intersection n - 1 (destination) in the shortest possible time.
Return the number of such shortest paths modulo 10⁹ + 7.
| n | roads | Expected Output | Description |
|---|---|---|---|
| 7 | [[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]] | 4 | There are 4 different shortest paths from 0 to 6 to 5 to 3 to 1 to 2 to 5 to 6 to 0 |
| 2 | [[0,1,1]] | 1 | Only one path from 0 to 1 |
| 4 | [[0,1,1],[1,2,1],[2,3,1],[0,3,3]] | 2 | Two paths of equal minimum length: 0→1→2→3 and 0→3 |
How it works
Understanding the Problem
We are given a graph with n nodes and a list of roads where each road connects two nodes and has a certain travel time. We are asked to find how many different ways we can reach the destination node n - 1 from the starting node 0 in the shortest possible time.
This is not just a shortest path problem — we also want to count all the different paths that result in the same minimum time. This means we need to track both the shortest distance and the number of ways to reach each node within that time.
Step-by-Step Solution with Example
step 1: Represent the graph
We first convert the input list of roads into an adjacency list so we can quickly look up all connected nodes and the time required to reach them. Each node points to a list of [neighbor, time] pairs.
step 2: Initialize data structures
We use two arrays:
dist[i]: Stores the shortest time needed to reach nodei. Initialize withInfinity, exceptdist[0] = 0.ways[i]: Stores the number of ways to reach nodeiin the shortest time. Initialize with 0, exceptways[0] = 1.
We also use a min-heap priority queue to always process the node with the least travel time first, just like in Dijkstra's algorithm.
step 3: Traverse the graph using Dijkstra’s algorithm
We repeatedly pop the node with the current shortest time from the priority queue. For each neighbor of that node, we calculate the new time required to reach it.
- If this new time is less than the currently known shortest time, we update
dist[neighbor]and setways[neighbor]equal toways[current]. - If the new time is equal to the currently known shortest time, it means we found another shortest path — so we add
ways[current]toways[neighbor].
step 4: Example walkthrough
Let’s say n = 4 and the roads are:
[[0, 1, 1],
[0, 2, 1],
[1, 3, 1],
[2, 3, 1]]
There are two shortest paths from 0 to 3:
- 0 → 1 → 3 (time 2)
- 0 → 2 → 3 (time 2)
We maintain:
dist[3] = 2(shortest time)ways[3] = 2(two ways)
This is how we track the number of ways while applying Dijkstra’s traversal.
Edge Cases
- No path exists: If the graph is disconnected and the destination is unreachable, the answer will be 0.
- Multiple edges between same nodes: We must check each one — one may lead to a faster route.
- Self-loops: These do not help in reducing distance to other nodes, but we should not crash if present.
- Large number of nodes and edges: Using efficient structures like min-heaps (priority queues) ensures optimal performance.
Finally
This problem builds upon Dijkstra’s algorithm, but adds a layer of counting the number of paths with the same shortest time. Understanding how and when to update both distance and path counts is crucial. By tracing each step carefully and considering edge cases, even a beginner can grasp this problem and apply it to similar graph-based scenarios.
Finally, remember to return ways[n - 1] as the result — which gives how many shortest paths reach the destination.
- Create an adjacency list from the
roadsarray. - Initialize
distarray to store shortest time to each node (set all to Infinity except source). - Initialize
waysarray to store number of ways to reach each node (set all to 0 except source). - Use a priority queue (min-heap) and start from node 0 with time 0.
- While the queue is not empty:
- Pop the node with minimum current time.
- For each neighbor of this node:
- If a new shorter time is found, update
distand setwaysto the current node's ways. - If the same time is found, increment
waysby current node's ways. - Return
ways[n - 1] % (10⁹ + 7).
Implementation and efficiency
C implementation
#include <stdio.h>
#include <stdlib.h>
#define MOD 1000000007
#define INF 1000000000
typedef struct {
int node;
int time;
} Pair;
int compare(const void *a, const void *b) {
return ((Pair *)a)->time - ((Pair *)b)->time;
}
int countPaths(int n, int roads[][3], int roadsSize) {
int dist[n], ways[n];
Pair heap[10000];
int heapSize = 0;
int adj[n][n], cost[n][n], degree[n];
for (int i = 0; i < n; i++) {
dist[i] = INF;
ways[i] = 0;
degree[i] = 0;
}
for (int i = 0; i < roadsSize; i++) {
int u = roads[i][0], v = roads[i][1], t = roads[i][2];
adj[u][degree[u]] = v;
cost[u][degree[u]++] = t;
adj[v][degree[v]] = u;
cost[v][degree[v]++] = t;
}
dist[0] = 0;
ways[0] = 1;
heap[heapSize++] = (Pair){0, 0};
while (heapSize) {
qsort(heap, heapSize, sizeof(Pair), compare);
Pair p = heap[--heapSize];
int node = p.node, time = p.time;
for (int i = 0; i < degree[node]; i++) {
int nei = adj[node][i];
int newTime = time + cost[node][i];
if (newTime < dist[nei]) {
dist[nei] = newTime;
ways[nei] = ways[node];
heap[heapSize++] = (Pair){nei, newTime};
} else if (newTime == dist[nei]) {
ways[nei] = (ways[nei] + ways[node]) % MOD;
}
}
}
return ways[n - 1];
}
int main() {
int roads[][3] = {{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}};
int result = countPaths(7, roads, 10);
printf("Number of shortest paths from 0 to 6: %d\n", result);
return 0;
}C++ implementation
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
const int MOD = 1e9 + 7;
int countPaths(int n, vector<vector<int>>& roads) {
vector<vector<pair<int,int>>> adj(n);
for (auto& r : roads) {
adj[r[0]].emplace_back(r[1], r[2]);
adj[r[1]].emplace_back(r[0], r[2]);
}
vector<long long> dist(n, 1e18), ways(n, 0);
priority_queue<pair<long long,int>, vector<pair<long long,int>>, greater<>> pq;
dist[0] = 0;
ways[0] = 1;
pq.push({0, 0});
while (!pq.empty()) {
auto [d, node] = pq.top(); pq.pop();
if (d > dist[node]) continue;
for (auto [nei, t] : adj[node]) {
long long newTime = d + t;
if (newTime < dist[nei]) {
dist[nei] = newTime;
ways[nei] = ways[node];
pq.push({newTime, nei});
} else if (newTime == dist[nei]) {
ways[nei] = (ways[nei] + ways[node]) % MOD;
}
}
}
return ways[n - 1];
}
int main() {
vector<vector<int>> 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}};
cout << "Number of shortest paths from 0 to 6: " << countPaths(7, roads) << endl;
return 0;
}Python implementation
import heapq
MOD = 10**9 + 7
def count_paths(n, roads):
adj = [[] for _ in range(n)]
for u, v, t in roads:
adj[u].append((v, t))
adj[v].append((u, t))
dist = [float('inf')] * n
ways = [0] * n
dist[0] = 0
ways[0] = 1
heap = [(0, 0)]
while heap:
time, node = heapq.heappop(heap)
for nei, t in adj[node]:
new_time = time + t
if new_time < dist[nei]:
dist[nei] = new_time
ways[nei] = ways[node]
heapq.heappush(heap, (new_time, nei))
elif new_time == dist[nei]:
ways[nei] = (ways[nei] + ways[node]) % MOD
return ways[n - 1]
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]]
print("Number of shortest paths from 0 to 6:", count_paths(7, roads))Java implementation
import java.util.*;
public class ShortestPathCount {
static final int MOD = 1_000_000_007;
public static int countPaths(int n, int[][] roads) {
List<List<int[]>> adj = new ArrayList<>();
for (int i = 0; i < n; i++) adj.add(new ArrayList<>());
for (int[] r : roads) {
adj.get(r[0]).add(new int[]{r[1], r[2]});
adj.get(r[1]).add(new int[]{r[0], r[2]});
}
long[] dist = new long[n];
int[] ways = new int[n];
Arrays.fill(dist, Long.MAX_VALUE);
dist[0] = 0;
ways[0] = 1;
PriorityQueue<long[]> pq = new PriorityQueue<>(Comparator.comparingLong(a -> a[0]));
pq.add(new long[]{0, 0});
while (!pq.isEmpty()) {
long[] top = pq.poll();
long d = top[0];
int node = (int) top[1];
if (d > dist[node]) continue;
for (int[] nei : adj.get(node)) {
int next = nei[0], t = nei[1];
long newTime = d + t;
if (newTime < dist[next]) {
dist[next] = newTime;
ways[next] = ways[node];
pq.add(new long[]{newTime, next});
} else if (newTime == dist[next]) {
ways[next] = (ways[next] + ways[node]) % MOD;
}
}
}
return ways[n - 1];
}
public static void main(String[] args) {
int[][] 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}};
System.out.println("Number of shortest paths from 0 to 6: " + countPaths(7, roads));
}
}JavaScript implementation
function countPaths(n, roads) {
const MOD = 1e9 + 7;
const adj = Array.from({ length: n }, () => []);
for (const [u, v, t] of roads) {
adj[u].push([v, t]);
adj[v].push([u, t]);
}
const dist = Array(n).fill(Infinity);
const ways = Array(n).fill(0);
const minHeap = [[0, 0]]; // [time, node]
dist[0] = 0;
ways[0] = 1;
while (minHeap.length > 0) {
minHeap.sort((a, b) => a[0] - b[0]);
const [time, node] = minHeap.shift();
for (const [nei, t] of adj[node]) {
const newTime = time + t;
if (newTime < dist[nei]) {
dist[nei] = newTime;
ways[nei] = ways[node];
minHeap.push([newTime, nei]);
} else if (newTime === dist[nei]) {
ways[nei] = (ways[nei] + ways[node]) % MOD;
}
}
}
return ways[n - 1];
}
const 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]];
console.log("Number of shortest paths from 0 to 6:", countPaths(7, roads));Go implementation
package main
import (
"container/heap"
"fmt"
)
const MOD = 1e9 + 7
type pair struct {
node, time int
}
type MinHeap []pair
func (h MinHeap) Len() int { return len(h) }
func (h MinHeap) Less(i, j int) bool { return h[i].time < h[j].time }
func (h MinHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *MinHeap) Push(x interface{}) { *h = append(*h, x.(pair)) }
func (h *MinHeap) Pop() interface{} {
old := *h
n := len(old)
x := old[n-1]
*h = old[0 : n-1]
return x
}
func countPaths(n int, roads [][]int) int {
adj := make([][]pair, n)
for _, r := range roads {
u, v, t := r[0], r[1], r[2]
adj[u] = append(adj[u], pair{v, t})
adj[v] = append(adj[v], pair{u, t})
}
dist := make([]int, n)
ways := make([]int, n)
for i := range dist {
dist[i] = 1e9
}
dist[0] = 0
ways[0] = 1
h := &MinHeap{{0, 0}}
heap.Init(h)
for h.Len() > 0 {
cur := heap.Pop(h).(pair)
node, time := cur.node, cur.time
if time > dist[node] {
continue
}
for _, nei := range adj[node] {
newTime := time + nei.time
if newTime < dist[nei.node] {
dist[nei.node] = newTime
ways[nei.node] = ways[node]
heap.Push(h, pair{nei.node, newTime})
} else if newTime == dist[nei.node] {
ways[nei.node] = (ways[nei.node] + ways[node]) % MOD
}
}
}
return ways[n-1]
}
func main() {
roads := [][]int{{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}}
fmt.Println("Number of shortest paths from 0 to 6:", countPaths(7, roads))
}Rust implementation
use std::collections::BinaryHeap;
use std::cmp::Reverse;
const MOD: i64 = 1_000_000_007;
fn count_paths(n: usize, roads: Vec<[usize; 3]>) -> i64 {
let mut adj = vec![vec![]; n];
for [u, v, t] in roads {
adj[u].push((v, t));
adj[v].push((u, t));
}
let mut dist = vec![i64::MAX; n];
let mut ways = vec![0; n];
let mut heap = BinaryHeap::new();
dist[0] = 0;
ways[0] = 1;
heap.push(Reverse((0, 0)));
while let Some(Reverse((time, node))) = heap.pop() {
if time > dist[node] { continue; }
for &(nei, t) in &adj[node] {
let new_time = time + t as i64;
if new_time < dist[nei] {
dist[nei] = new_time;
ways[nei] = ways[node];
heap.push(Reverse((new_time, nei)));
} else if new_time == dist[nei] {
ways[nei] = (ways[nei] + ways[node]) % MOD;
}
}
}
ways[n - 1]
}
fn main() {
let roads = vec![[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]];
println!("Number of shortest paths from 0 to 6: {}", count_paths(7, roads));
}Kotlin implementation
import java.util.*
const val MOD = 1_000_000_007
fun countPaths(n: Int, roads: Array<IntArray>): Int {
val adj = Array(n) { mutableListOf<Pair<Int, Int>>() }
for ((u, v, t) in roads) {
adj[u].add(v to t)
adj[v].add(u to t)
}
val dist = LongArray(n) { Long.MAX_VALUE }
val ways = IntArray(n)
val pq = PriorityQueue(compareBy<Pair<Long, Int>> { it.first })
dist[0] = 0L
ways[0] = 1
pq.add(0L to 0)
while (pq.isNotEmpty()) {
val (time, node) = pq.poll()
if (time > dist[node]) continue
for ((nei, t) in adj[node]) {
val newTime = time + t
if (newTime < dist[nei]) {
dist[nei] = newTime
ways[nei] = ways[node]
pq.add(newTime to nei)
} else if (newTime == dist[nei]) {
ways[nei] = (ways[nei] + ways[node]) % MOD
}
}
}
return ways[n - 1]
}
fun main() {
val roads = arrayOf(
intArrayOf(0,6,7), intArrayOf(0,1,2), intArrayOf(1,2,3), intArrayOf(1,3,3),
intArrayOf(6,3,3), intArrayOf(3,5,1), intArrayOf(6,5,1), intArrayOf(2,5,1),
intArrayOf(0,4,5), intArrayOf(4,6,2)
)
println("Number of shortest paths from 0 to 6: ${countPaths(7, roads)}")
}Swift implementation
import Foundation
let MOD = 1_000_000_007
func countPaths(_ n: Int, _ roads: [[Int]]) -> Int {
var adj = Array(repeating: [(Int, Int)](), count: n)
for road in roads {
let (u, v, t) = (road[0], road[1], road[2])
adj[u].append((v, t))
adj[v].append((u, t))
}
var dist = Array(repeating: Int.max, count: n)
var ways = Array(repeating: 0, count: n)
var heap = [(time: Int, node: Int)]()
dist[0] = 0
ways[0] = 1
heap.append((0, 0))
while !heap.isEmpty {
heap.sort { $0.time < $1.time }
let (time, node) = heap.removeFirst()
for (nei, t) in adj[node] {
let newTime = time + t
if newTime < dist[nei] {
dist[nei] = newTime
ways[nei] = ways[node]
heap.append((newTime, nei))
} else if newTime == dist[nei] {
ways[nei] = (ways[nei] + ways[node]) % MOD
}
}
}
return ways[n - 1]
}
let 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]]
print("Number of shortest paths from 0 to 6:", countPaths(7, roads))TypeScript implementation
type Edge = [number, number];
function countPaths(n: number, roads: number[][]): number {
const MOD = 1e9 + 7;
const adj: Edge[][] = Array.from({ length: n }, () => []);
for (const [u, v, t] of roads) {
adj[u].push([v, t]);
adj[v].push([u, t]);
}
const dist: number[] = Array(n).fill(Infinity);
const ways: number[] = Array(n).fill(0);
const minHeap: [number, number][] = [[0, 0]];
dist[0] = 0;
ways[0] = 1;
while (minHeap.length > 0) {
minHeap.sort((a, b) => a[0] - b[0]);
const [time, node] = minHeap.shift()!;
for (const [nei, t] of adj[node]) {
const newTime = time + t;
if (newTime < dist[nei]) {
dist[nei] = newTime;
ways[nei] = ways[node];
minHeap.push([newTime, nei]);
} else if (newTime === dist[nei]) {
ways[nei] = (ways[nei] + ways[node]) % MOD;
}
}
}
return ways[n - 1];
}
const 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]];
console.log("Number of shortest paths from 0 to 6:", countPaths(7, roads));