Dynamic Interval Alignment Resolver 4 — Problem Statement & Solution Guide
Problem Description
You are given an N x N matrix dist representing the shortest path distances between N nodes in a fully connected graph. The matrix is initialized with direct edge weights, where dist[i][j] is the weight of the edge from node i to node j, and dist[i][i] is 0. If no direct edge exists, the value is represented by a large integer INF.
Your task is to compute the all-pairs shortest paths in this graph. Specifically, you must determine the minimum possible distance from every node i to every node j, allowing for intermediate nodes. This involves updating the distance matrix such that dist[i][j] holds the shortest path length from i to j after considering all possible intermediate nodes.
Return the updated N x N matrix where each entry dist[i][j] represents the shortest distance from node i to node j. If no path exists between two nodes, the value should remain INF.
DSA Pattern Breakdown
DSA Pattern Breakdown
"Dynamic Interval Alignment Resolver 4"
WHY DOES IT MATTER?
All‑pairs shortest paths are a foundational graph pattern used in routing, network reliability analysis, and many DP problems. Mastering Floyd‑Warshall demonstrates the ability to convert a global optimization problem into a series of local updates.
OPTIMIZATION CHALLENGE
The key insight is recognizing that the optimal path can be built by iteratively allowing one more intermediate node, turning a combinatorial explosion into a deterministic triple‑nested loop with O(1) per update.
REAL-WORLD CONNECTION
Think of a logistics company that wants the cheapest shipping cost between every pair of warehouses. Instead of recomputing routes for each origin‑destination pair, the company precomputes a cost matrix once, analogous to Floyd‑Warshall’s matrix updates.
When coding, keep the loops in the order k → i → j to ensure that when you use dist[i][k] and dist[k][j], they already reflect paths that may use earlier intermediates only.
COMPLEXITY AT A GLANCE
O(N³)O(N²)Core Theory — Why This Approach?
The Floyd‑Warshall algorithm is a dynamic‑programming technique that solves the all‑pairs shortest‑path problem on weighted directed graphs (including negative edge weights but no negative cycles) in Θ(N³) time. It incrementally improves the distance matrix by considering each node k as an intermediate waypoint and updating dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]). This recurrence captures the optimal substructure: the shortest path from i to j either avoids k or passes through k, and the sub‑paths themselves must be optimal.
A naive approach would run Dijkstra’s algorithm from every source node, which costs O(N·(E log N)) and degrades to O(N³ log N) for dense graphs where E ≈ N². In a fully connected matrix representation, the overhead of priority queues and adjacency lists becomes unnecessary, and the cubic DP loop of Floyd‑Warshall dominates with a lower constant factor. Moreover, Floyd‑Warshall works uniformly for both positive and negative edge weights, making it the optimal paradigm for dense, matrix‑based inputs.
Interview Questions on This Problem
Q1How does Floyd‑Warshall handle negative edge weights, and what condition must be checked after the algorithm finishes?
The DP recurrence naturally accommodates negative weights because it only adds distances; however, if a negative cycle exists, the diagonal entry dist[i][i] will become negative after the final iteration. Detecting any dist[i][i] < 0 signals a negative cycle.
Q2Why is Floyd‑Warshall preferred over N runs of Dijkstra when the graph is represented as an adjacency matrix?
In an adjacency matrix the graph is dense (E ≈ N²). Dijkstra with a binary heap runs in O(N² log N) per source, leading to O(N³ log N) total, whereas Floyd‑Warshall runs in pure O(N³) with simple triple loops and no heap overhead, giving a better constant factor.
Q3Can Floyd‑Warshall be adapted to also reconstruct the actual shortest paths, and if so, how?
Yes. Maintain a predecessor matrix pred[i][j] initialized to i (or -1). Whenever dist[i][j] is updated via k, set pred[i][j] = pred[k][j]. After the algorithm, the path from i to j can be rebuilt by back‑tracking from j using pred until reaching i.
Examples
Input
dist = [[0, 5, INF], [INF, 0, 3], [INF, INF, 0]]
Output
[[0, 5, 8], [INF, 0, 3], [INF, INF, 0]]
Explanation: Initially, the direct distances are given. We check if going through intermediate nodes reduces any distance. For node 0 to node 2, the direct path is INF. However, going 0 -> 1 -> 2 costs 5 + 3 = 8, which is less than INF. Thus, dist[0][2] becomes 8. No other paths are improved. The final matrix reflects these updates.
Input
dist = [[0, 1, 4], [2, 0, 3], [5, 6, 0]]
Output
[[0, 1, 4], [2, 0, 3], [5, 6, 0]]
Explanation: We evaluate all pairs. For example, path 0 -> 1 -> 2 costs 1 + 3 = 4, which is equal to the direct path 0 -> 2 (4), so no change. Path 2 -> 0 -> 1 costs 5 + 1 = 6, equal to direct 2 -> 1 (6). All existing direct paths are already optimal or tied with indirect paths. The matrix remains unchanged.
Input
dist = [[0, INF, 10], [INF, 0, 5], [10, 5, 0]]
Output
[[0, 15, 10], [15, 0, 5], [10, 5, 0]]
Explanation: Direct path 0 -> 1 is INF. However, 0 -> 2 -> 1 costs 10 + 5 = 15. So dist[0][1] becomes 15. Similarly, 1 -> 0 -> 2 costs INF + 10 = INF, but 1 -> 2 -> 0 costs 5 + 10 = 15. So dist[1][0] becomes 15. The symmetric nature is preserved. Final matrix has 15 for the previously disconnected pairs.
Constraints
- 1 <= N <= 100
- 0 <= dist[i][j] <= 10^4
- dist[i][i] == 0 for all i
- dist[i][j] == INF if no direct edge exists, where INF = 10^9
Optimal Approach & Strategy
Apply Floyd‑Warshall: three nested loops over k, i, j updating the distance matrix in place, achieving O(N³) time and O(N²) space.
Brute Force Approach
Run a separate shortest‑path algorithm (like BFS for unweighted or Dijkstra for weighted) from each source node, yielding O(N·(E log N)) time.
Verified Code Solutions
function solution(nums) {
let maxSum = nums[0];
let currentSum = nums[0];
for (let i = 1; i < nums.length; i++) {
currentSum = Math.max(nums[i], currentSum + nums[i]);
maxSum = Math.max(maxSum, currentSum);
}
return maxSum;
}class Solution {
public:
int solution(vector<int>& nums) {
int maxSum = nums[0];
int currentSum = nums[0];
for (int i = 1; i < nums.size(); i++) {
currentSum = max(nums[i], currentSum + nums[i]);
maxSum = max(maxSum, currentSum);
}
return maxSum;
}
};class Solution {
public int solution(int[] nums) {
int maxSum = nums[0];
int currentSum = nums[0];
for (int i = 1; i < nums.length; i++) {
currentSum = Math.max(nums[i], currentSum + nums[i]);
maxSum = Math.max(maxSum, currentSum);
}
return maxSum;
}
}def solution(nums):
max_sum = nums[0]
current_sum = nums[0]
for i in range(1, len(nums)):
current_sum = max(nums[i], current_sum + nums[i])
max_sum = max(max_sum, current_sum)
return max_sumfunction solution(nums) {
let maxSum = nums[0];
let currentSum = nums[0];
for (let i = 1; i < nums.length; i++) {
currentSum = Math.max(nums[i], currentSum + nums[i]);
maxSum = Math.max(maxSum, currentSum);
}
return maxSum;
}Asked in Top Tech Interviews
Solve in Interative Editor
Ready to test your code? Open our built-in compiler, run custom test suites, and see detailed complexity analysis reports instantly.