Backtracking · 15 of 19
Find if there is a path of more than k length from a source
Starting from one stop on a map of roads, find out if you can walk farther than k without ever visiting a stop twice.
- Simple way
- time O(V!)
room O(V) - Clever way
- time O(V!)
room O(V)
The problem, simply
You get a map: stops joined by two-way roads, and each road has a length. You also get a starting stop and a number k.
Is there a walk that starts at the starting stop, never visits the same stop twice, and has a total length more than k? Answer yes or no. You do not have to end anywhere special.
A story to remember it
Picture it
Five stops. The number on each road is its length. We start at stop 0. The longest walk from 0 without repeats is 0, 3, 4, which is 5 + 6 = 11.
- looking now
Examples
A long enough walk exists
- In
the map above, source = 0, k = 10- Out
true
0 to 3 to 4 has length 5 + 6 = 11, and 11 is more than 10.
Just not enough
- In
the map above, source = 0, k = 11- Out
false
The longest walk from 0 is 11. We need more than 11, so the answer is no. Equal is not enough.
A lonely stop
- In
one stop and no roads, source = 0, k = 0- Out
false
You cannot walk at all, so the length stays 0, and 0 is not more than 0.
A loop does not help
- In
roads 0-1:4, 1-2:4, 2-0:4, source = 0, k = 8- Out
false
The best walk is 0, 1, 2 with length 8. Going on to 0 would make 12, but 0 was already visited, so that is not allowed.
Way 1: the simple way
Find the longest walk from the start, then compare it with k.
- Mark the current stop as used.
- For each road to a stop that is not used, find the longest walk from that stop, and add the road length.
- Keep the biggest. Unmark the stop on the way out, so other walks may use it.
- At the end, answer
longest > k.
Every walk from 0 without repeats. The small number under each stop is the length so far. Way 1 walks the whole tree to find the biggest, 11.
- answer
import java.util.*;
class Solution {
// graph.get(u) holds roads as {v, length}
public boolean pathMoreThanK(List<List<int[]>> graph, int source, int k) {
boolean[] used = new boolean[graph.size()];
return longest(graph, source, used) > k;
}
// the longest walk that starts at u and never repeats a stop
private int longest(List<List<int[]>> graph, int u, boolean[] used) {
used[u] = true;
int best = 0;
for (int[] road : graph.get(u)) {
int v = road[0], len = road[1];
if (!used[v]) best = Math.max(best, len + longest(graph, v, used));
}
used[u] = false; // free the stop for other walks
return best;
}
}How long it takes: in a map where every stop touches every other, the number of walks without repeats is about (V - 1)!, so this is O(V!). Extra room: the used marks and the call stack, O(V).
Way 2: the clever way
We do not need the longest walk. We only need one walk that is long enough. So carry the length as you walk, and stop the whole search the first moment it goes past k.
- Start at the source with length 0, and mark it used.
- If the length is more than
k, answer yes right away. - Otherwise try each road to an unused stop: mark it, walk, and if that walk said yes, say yes too.
- If the walk said no, unmark the stop and try the next road. This undo is the backtracking.
- If no road works, say no.
Walk, add, and step back when stuck
01 / 15
Start at 0 with length 0. We need a path longer than 10, and no stop may be used twice.
- path length
- 0
- k
- 10
- looking now
- answer
- rejected
- path
import java.util.*;
class Solution {
public boolean pathMoreThanK(List<List<int[]>> graph, int source, int k) {
boolean[] used = new boolean[graph.size()];
used[source] = true;
return walk(graph, source, 0, k, used);
}
private boolean walk(List<List<int[]>> graph, int u, int total, int k, boolean[] used) {
if (total > k) return true; // long enough, stop everything
for (int[] road : graph.get(u)) {
int v = road[0], len = road[1];
if (used[v]) continue; // never visit a stop twice
used[v] = true;
if (walk(graph, v, total + len, k, used)) return true;
used[v] = false; // step back and free the stop
}
return false;
}
// helper: build the two-way map from {a, b, length} triples
static List<List<int[]>> build(int n, int[][] roads) {
List<List<int[]>> g = new ArrayList<>();
for (int i = 0; i < n; i++) g.add(new ArrayList<>());
for (int[] r : roads) {
g.get(r[0]).add(new int[]{r[1], r[2]});
g.get(r[1]).add(new int[]{r[0], r[2]});
}
return g;
}
}How long it takes: still O(V!) in the worst case, for example when the answer is no and every walk must be tried. When the answer is yes, it often stops very early. Extra room: O(V) for the marks and the call stack.
There is no fast trick here. Finding the longest walk without repeats is one of the famous hard problems (it is NP-hard), so nobody knows a way that is always quick.
Watch out for
- Using
>=instead of>. The question says more thank. - Forgetting to mark the source. A walk could come back to it, which is a repeat.
- Returning from the loop without unmarking when the answer is no. The marks must be clean for the next road.
- In the yes case you may return without unmarking. That is fine because the search is over.
What interviewers ask next
- Why not use Dijkstra with negative lengths to find the longest walk? Flipping the signs makes negative loops, and shortest path methods break on those. The problem is NP-hard, so no such shortcut exists.
- What if the map has one-way roads and no loops (a DAG)? Then walks can never repeat a stop. Go through the stops in topological order and keep the longest length to each stop, in
O(V + E). - What if
Vis at most about 20? Use a bitmask table:best[mask][v]is the longest walk that used exactly the stops inmaskand ends atv. That isO(2^V * V^2), much better thanV!. - Print the walk too. Keep a list, add a stop when you mark it and remove it when you unmark it. When the answer is yes, the list holds the walk.
Remember this
Grown-up words: stops are vertices, roads are weighted edges, a walk with no repeats is a simple path, the used marks are a visited array that is reset on the way back, which is backtracking, and the tree of walks is the recursion tree of a depth first search.