Restatement: in a weighted graph, find the maximum-cost path from a
source to any other vertex whose total cost exceeds a given K; the path must not
contain a cycle. Weights may be negative.
Practice: GfG — "Maximum cost path from source to destination
greater than given cost". (No direct LeetCode equivalent.)
Pattern / family: BFS carrying rich per-path state (running cost +
visited set).
⭐ TRICK — why BFS, not DFS (the key point): at each
node you want to consider ALL outgoing edges and track the best running cost, rather
than committing to one deep dive. That "stay at the current node, fan out over every
edge, keep the max" behavior is BFS. DFS commits to diving deep before it has
compared alternatives. Negative weights make greedy/early-cutoff wrong, so you must
explore all edge options — BFS with state does this cleanly.
Approach: the queue holds a triple — (current vertex, cost
accumulated so far, set of vertices visited on this path). On each pop: if
cost > K, update the global max. Then for every outgoing edge to an
unvisited (per this path) vertex, push the neighbor with
cost + edgeWeight and a copied visited-set (to forbid cycles).
Complexity: often quoted as O(V·E). [!]
That figure is misleading; because this enumerates acyclic paths it is
exponential in the worst case (path enumeration), not truly O(V·E). Trust O(V·E)
only as the per-relaxation framing, not a tight bound. Space O(V) per queued state.
class State {
int vertex, cost;
Set<Integer> visited;
State(int v, int c, Set<Integer> vis) { vertex = v; cost = c; visited = vis; }
}
int findMaxCost(List<int[]>[] adj, int src, int K) {
Queue<State> q = new LinkedList<>();
Set<Integer> start = new HashSet<>();
start.add(src);
q.add(new State(src, 0, start));
int maxCost = Integer.MIN_VALUE;
while (!q.isEmpty()) {
State cur = q.poll();
if (cur.cost > K) maxCost = Math.max(maxCost, cur.cost);
for (int[] edge : adj[cur.vertex]) { // edge = {destination, weight}
int dest = edge[0], w = edge[1];
if (!cur.visited.contains(dest)) { // no cycle on this path
Set<Integer> nextVis = new HashSet<>(cur.visited);
nextVis.add(dest);
q.add(new State(dest, cur.cost + w, nextVis));
}
}
}
return maxCost;
}
💡 TIP — the walkthrough: trace it: from 0 pick the
11-edge over the 5-edge; from 6 pick the 9-edge, etc. — always comparing all edges
out of the current node and keeping the max running total. That
comparison-at-each-node is precisely the BFS advantage here.
⚠ PITFALL: the visited set must be COPIED per branch, not shared. A
single global visited set would forbid a vertex on paths that never actually
contained it. Copying is what makes "no cycle on this path" correct.
Interview follow-ups: for the shortest-path mirror (minimum cost),
the same BFS-with-state skeleton applies but you track the min; on graphs without
negative weights, Dijkstra is the specialized tool.
Why this matters: "BFS carrying path state" generalizes to
constrained-path problems (max/min cost with forbidden revisits, path-with-budget,
k-stop routing).