Number of Ways to Arrive at Destination

MediumGraphs · Dijkstra + Path Counting DP🧩 Tree & Graph DPLeetCode #1976 ↗Updated

Problem

A city has n intersections 0..n-1 joined by two-way roads, where roads[i] = [u, v, time]. Every intersection is reachable. Count how many different routes go from 0 to n-1 using the minimum possible total time, and return that count modulo 109+7.
Inputn=4, roads=[[0,1,1],[0,2,1],[1,3,1],[2,3,1]]Output2
Inputn=4, roads=[[0,1,1],[1,2,1],[0,2,2],[2,3,5],[0,3,9]]Output2
Inputn=1, roads=[]Output1

1 ≤ n ≤ 200  ·  n-1 ≤ roads.length ≤ n(n-1)/2  ·  1 ≤ time ≤ 109  ·  at most one road per pair  ·  answer mod 109+7

Try it yourself

Java
class Solution {
    public int countPaths(int n, int[][] roads) {
        
    }
}
Your code is saved in this browser.Submit on LeetCode ↗
Key insight
With positive weights, every shortest route to v ends with an edge u→v where dist[u] + w = dist[v]. So ways[v] = Σ ways[u] over those “tight” edges — and Dijkstra pops nodes in exactly the order that makes this sum final.
PatternTree & Graph DP

Instinct A node's best answer depends only on its children's answers - post-order DFS that returns a tiny tuple of states per node.

Memory hack Kids report up, parent decides - every subtree hands its parent a small report card, and the parent never re-reads the kids' homework.

Open the Tree & Graph DP guide →

Approaches

Brute ForceDFS over All Simple Paths

Time O(n!)Space O(n²)

Positive weights mean a shortest route never repeats a node, so enumerate every simple path from 0 with backtracking. Keep the best total seen: a strictly shorter arrival resets the counter, an equal one increments it, and any partial path already longer than the best is cut off.

Java
private static final int MOD = 1_000_000_007;
private long best;
private long ways;

public int countPaths(int n, int[][] roads) {
    long[][] w = new long[n][n]; // 0 = no road
    for (int[] r : roads) {
        w[r[0]][r[1]] = r[2];
        w[r[1]][r[0]] = r[2];
    }
    best = Long.MAX_VALUE;
    ways = 0;
    dfs(0, n - 1, 0L, new boolean[n], w);
    return (int) ways;
}

private void dfs(int u, int target, long dist, boolean[] onPath, long[][] w) {
    if (dist > best) return; // already slower than the best route
    if (u == target) {
        if (dist < best) {
            best = dist;
            ways = 0;
        }
        ways = (ways + 1) % MOD;
        return;
    }
    onPath[u] = true;
    for (int v = 0; v < w.length; v++)
        if (w[u][v] > 0 && !onPath[v]) dfs(v, target, dist + w[u][v], onPath, w);
    onPath[u] = false;
}

Easy to trust and great for testing, but the number of simple paths explodes.

At a glance

LevelApproachTimeSpace
Brute ForceDFS over All Simple PathsO(n!)O(n²)
BetterFloyd–Warshall, then Count in Distance OrderO(n³)O(n²)
OptimalDijkstra with a Ways ArrayO((n + E) log n)O(n + E)
Common pitfalls
  • Using int distances — sums reach ~2·1011 and overflow.
  • Adding to ways[v] on a strictly shorter path instead of resetting it.
  • Comparing floating/approximate distances or skipping the stale-entry check, which double-counts.
Interview tips
  • Say it upfront: “Dijkstra plus a DP on the shortest-path DAG”.
  • Explain why positive weights guarantee ways[u] is final when u is popped.
  • Mention the modulo and long distances before you start coding.
Pattern
Count shortest paths → Dijkstra/BFS that keeps a ways[] array: reset on improvement, add on tie.
My notes saved in this browser