Algorithm for finding the path that minimizes the maximum weight between two nodes - algorithm

I would like to travel by car from city X to city Y. My car has a small tank, and gas stations exist only at intersections of the roads (the intersections are nodes and the roads are edges). Therefore, I would like to take a path such that the maximum distance that I drive between two gas stations is minimized. What efficient algorithm can I use to find that path? Brute force is one bad solution. I am wondering if there exists a more efficient algorithm.

Here is a simple solution:
Sort the edges by their weights.
Start adding them one by one(from the lightest to the heaviest) until X and Y become connected.
To check if they are connected, you can use a union-find data structure.
The time complexity is O(E log E).
A proof of correctness:
The correct answer is not larger than the one returned by this solution. It is the case because the solution is constructive: once X and Y are in the same component, we can explicitly write down the path between them. It cannot contain heavier edges because they haven't been added yet.
The correct answer is not smaller than the one returned by this solution. Let's assume that there is a path between X and Y that consists of edges which have weight strictly less than the returned answer. But is not possible as all lighter edges were processed before(we iterate over them in the sorted order) and X and Y were in different components. Thus, there was no path between them.
1) and 2) imply the correctness of this algorithm.
This solution works for undirected graphs.
Here is an algorithms which solves the problem for a directed case(it works for undirected graphs, too):
Let's sort the edges by their weights.
Let's binary search over the weight of the heaviest edge in the path(it is determined by an index of the edge in the sorted list of all edges).
For a fixed answer candidate i, we can do the following:
Add all edges with indices up to i in the sorted list(that is, all edges which are not heavier than the current one).
Run DFS or BFS to check that there is a path from X to Y.
Adjust left and right borders in the binary search depending on the existence of such path.
The time complexity is O((E + V) * log E)(we run DFS/BFS log E times and each of them is done in O(E + V) time).
Here is a pseudo code:
if (X == Y)
return 0 // We don't need any edges.
if (Y is not reachable from X using all edges)
return -1 // No solution.
edges = a list of edges sorted by their weight in increasing order
low = -1 // definitely to small(no edges)
high = edges.length - 1 // definitely big enough(all edges)
while (high - low > 1)
mid = low + (high - low) / 2
g = empty graph
for i = 0...mid
g.add(edges[i])
if (g.hasPath(X, Y)) // Checks that there is a path using DFS or BFS
high = mid
else
low = mid
return edges[high]

Related

Shortest path from one source which goes through N edges

In my economics research I am currently dealing with a specific shortest path problem:
Given a directed deterministic dynamic graph with weights on the edges, I need to find the shortest path from one source S, which goes through N edges. The graph can have cycles, the edge weights could be negative, and the path is allowed to go through a vertex or edge more than once.
Is there an efficient algorithm for this problem?
One possibility would be:
First find the lowest edge-weight in the graph.
And then build a priority queue of all paths from the starting edge (initially an empty path from starting point) where all yet-to-be-handled edges are counted as having the lowest weight.
Main loop:
Remove path with lowest weight from the queue.
If path has N edges you are done
Otherwise add all possible one-edge extensions of that path to priority queue
However, that simple algorithm has a flaw - you might re-visit a vertex multiple times as i:th edge (visiting as 2nd and 4th is ok, but 4th in two different paths is the issue), which is inefficient.
The algorithm can be improved by skipping them in the 3rd step above, since the priority queue guarantees that the first partial path to the vertex had the lowest weight-sum to that vertex, and the rest of the path does not depend on how you reached the vertex (since edges and vertices can be duplicated).
The "exactly N edges" constraint makes this problem much easier to solve than if that constraint didn't exist. Essentially you can solve N = 0 (just the start node), use that to solve N = 1 (all the neighbors of the start node), then N = 2 (neighbors of the solution to N = 1, taking the lowest cost path for nodes that are are connected to multiple nodes), etc.
In pseudocode (using {field: val} to mean "a record with a field named field with value val"):
# returns a map from node to cost, where each key represents
# a node reachable from start_node in exactly n steps, and the
# associated value is the total cost of the cheapest path to
# that node
cheapest_path(n, start_node):
i = 0
horizon = new map()
horizon[start_node] = {cost: 0, path: []}
while i <= n:
next_horizon = new map()
for node, entry in key_value_pairs(horizon):
for neighbor in neighbors(node):
this_neighbor_cost = entry.cost + edge_weight(node, neighbor, i)
this_neighbor_path = entry.path + [neighbor]
if next_horizon[neighbor] does not exist or this_neighbor_cost < next_horizon[neighbor].cost:
next_horizon[neighbor] = {cost: this_neighbor_cost, path: this_neighbor_path}
i = i + 1
horizon = next_horizon
return horizon
We take account of dynamic weights using edge_weight(node, neighbor, i), meaning "the cost of going from node to neighbor at time step i.
This is a degenerate version of a single-source shortest-path algorithm like Dijkstra's Algorithm, but it's much simpler because we know we must walk exactly N steps so we don't need to worry about getting stuck in negative-weight cycles, or longer paths with cheaper weights, or anything like that.

Algorithm for finding spanning tree with minimum range in a given graph

Given a weighted undirected graph G(v,e) with weights w(e), find the set of edges such that each pair of vertices (u,v)∈G are connected (in short, spanning tree) and the range of weights of selected edges is minimum (or the difference between the minimum weight and the maximum weight is minimum).
I tried greedy approach in which sorted the edges with respect to weights and then selected two edges with minimum weight difference between the consecutive edges (g[index = current_left],g[index+1 = current_right]) in the sorted array, subsequently I moved left or right depending on the minimum difference between the (current_left,current_left-j) or (current_right,current_right+j) where j is incremented till we find an edge with at least one non-visited vertex.
For example:
Here the minimum range that we can get is by selecting edges with weight {2,3,5} and the range is 3.
Please point a test case where the suggested algorithm fails and suggest an algorithm for finding such spanning tree.
Edit:
Expected time complexity is O(|E|log|E|) where |E| is number of edges.
You should be able to do it in O(E * (cost of MST computation)):
T = no tree
for all edge weights w_fix sorted in ascending order:
for all edges e:
if w(e) >= w_fix:
set w'(e) = w(e) - w_fix
else:
set w'(e) = infinity
find MST T' according to w'
if T == no tree or max edge weight(T) > max edge weight(T'):
set T := T'
print T
The idea is that some edge weight has to be the minimum edge weight among the edges in an optimal spanning tree; so fix a minimum edge weight and find an MST that only contains edges heavier than that. Since all MSTs are also minimum bottleneck spanning trees, this will work.
Here's an improvement that is optimal up to a log-square factor; the basic idea remains the same.
sort edge array E[] by increasing weights
low := high := 0
opt_low := opt_high := 0
opt := infinity
connected := false
while (high < E.length - 1) or (connected):
if not connected:
high = high + 1
else:
low = low + 1
update(connected)
if connected:
if E[high].weight - E[low].weight < opt:
opt = E[high].weight - E[low].weight
opt_low = low
opt_high = high
print(opt, opt_low, opt_high)
The idea is to keep a sliding window over the edges and use connectivity to maintain the window. To maintain connectivity information, you would use special data structures. There's a number of them that allow for polylogarithmic time costs to maintain connectivity information for both deleting and adding edges, and you can find information on those data structures in these MIT 6.851 lecture notes.
The algorithm described by G Bach infact works correctly and has a run time of O(m*m) where m is the number of edges(considering the computation of mst takes O(m) time). This was a question asked in codeforces edu section.

Find a path in a complete graph with cost limit and max reward

I'm looking for an algorithm to solve this problem. I have to implement it (so I need a not np solution XD)
I have a complete graph with a cost on each arch and a reward on each vertex. I have only a start point, but it doesn't matter the end point, becouse the problem is to find a path to see as many vertex as possible, in order to have the maximum reward possible, but subject to a maximum cost limit. (for this reason it doesn't matter the end position).
I think to find the optimum solution is a np-hard problem, but also an approximate solution is apprecciated :D
Thanks
I'm trying study how to solve the problem with branch & bound...
update: complete problem dscription
I have a region in which there are several areas identify by its id and x,y,z position. Each vertex identifies one ot these areas. The maximum number of ares is 200.
From a start point S, I know the cost, specified in seconds and inserted in the arch (so only integer values), to reach each vertex from each other vertex (a complete graph).
When I visit a vertex I get a reward (float valiues).
My objective is to find a paths in a the graph that maximize the reward but I'm subject to a cost constraint on the paths. Indeed I have only limited minutes to complete the path (for example 600 seconds.)
The graph is made as matrix adjacency matrix for the cost and reward (but if is useful I can change the representation).
I can visit vertex more time but with one reward only!
Since you're interested in branch and bound, let's formulate a linear program. Use Floyd–Warshall to adjust the costs minimally downward so that cost(uw) ≤ cost(uv) + cost(vw) for all vertices u, v, w.
Let s be the starting vertex. We have 0-1 variables x(v) that indicate whether vertex v is part of the path and 0-1 variables y(uv) that indicate whether the arc uv is part of the path. We seek to maximize
sum over all vertices v of reward(v) x(v).
The constraints unfortunately are rather complicated. We first relate the x and y variables.
for all vertices v ≠ s, x(v) - sum over all vertices u of y(uv) = 0
Then we bound the cost.
sum over all arcs uv of cost(uv) y(uv) ≤ budget
We have (pre)flow constraints to ensure that the arcs chosen look like a path possibly accompanied by cycles (we'll handle the cycles shortly).
for all vertices v, sum over all vertices u of y(uv)
- sum over all vertices w of y(vw)
≥ -1 if v = s
0 if v ≠ s
To handle the cycles, we add cut covering constraints.
for all subsets of vertices T such that s is not in T,
for all vertices t in T,
x(t) - sum over all vertices u not in T and v in T of y(uv) ≥ 0
Because of the preflow constraints, a cycle necessarily is disconnected from the path structure.
There are exponentially many cut covering constraints, so when solving the LP, we have to generate them on demand. This means finding the minimum cut between s and each other vertex t, then verifying that the capacity of the cut is no greater than x(t). If we find a violation, then we add the constraint and use the dual simplex method to find the new optimum (repeat as necessary).
I'm going to pass on describing the branching machinery – this should be taken care of by your LP solver anyway.
Finding the optimal solution
Here is a recursive approach to solving your problem.
Let's begin with some definitions :
Let A = (Ai)1 ≤ i ≤ N be the areas.
Let wi,j = wj,i the time cost for traveling from Ai to Aj and vice versa.
Let ri the reward for visiting area Ai
Here is the recursive procedure that will output the exact requested solution : (pseudo-code)
List<Area> GetBestPath(int time_limit, Area S, int *rwd) {
int best_reward(0), possible_reward(0), best_fit(0);
List<Area> possible_path[N] = {[]};
if (time_limit < 0) {
return [];
}
if (!S.visited) {
*rwd += S.reward;
S.visit();
}
for (int i = 0; i < N; ++i) {
if (S.index != i) {
possible_path[i] = GetBestPath(time_limit - W[S.index][i], A[i], &possible_reward);
if (possible_reward > best_reward) {
best_reward = possible_reward;
best_fit = i;
}
}
}
*rwd+= best_reward;
possible_path[best_fit].push_front(S);
return possible_path[best_fit];
}
For obvious clarity reasons, I supposed the Ai to be globally reachable, as well as the wi,j.
Explanations
You start at S. First thing you do ? Collect the reward and mark the node as visited. Then you have to check which way to go is best between the S's N-1 neighbors (lets call them NS,i for 1 ≤ i ≤ N-1).
This is the exact same thing as solving the problem for NS,i with a time limit of :
time_limit - W(S ↔ NS,i)
And since you mark the visited nodes, when arriving at an area, you first check if it is marked. If so you have no reward ... Else you collect and mark it as visited ...
And so forth !
The ending condition is when time_limit (C) becomes negative. This tells us we reached the limit and cannot proceed to further moves : the recursion ends. The final path may contain useless journeys if all the rewards have been collected before the time limit C is reached. You'll have to "prune" the output list.
Complexity ?
Oh this solution is soooooooo awful in terms of complexity !
Each calls leads to N-1 calls ... Until the time limit is reached. The longest possible call sequence is yielded by going back and forth each time on the shortest edge. Let wmin be the weight of this edge.
Then obviously, the overall complexity is bounded by NC/wmin.C/wmin.
This is huuuuuge.
Another approach
Maintain a hash table of all the visited nodes.
On the other side, maintain a Max-priority queue (eg. using a MaxHeap) of the nodes that have not been collected yet. (The top of the heap is the node with the highest reward). The priority value for each node Ai in the queue is set as the couple (ri, E[wi,j])
Pop the heap : Target <- heap.pop().
Compute the shortest path to this node using Dijkstra algorithm.
Check out the path : If the cost of the path is too high, then the node is not reachable, add it to the unreachable nodes list.
Else collect all the uncollected nodes that you find in it and ...
Remove each collected node from the heap.
Set Target as the new starting point.
In either case, proceed to step 1. until the heap is empty.
Note : A hash table is the best suited to keep track of the collected node. This way, we can check a node in a path computed using Dijkstra in O(1).
Likewise, maintaining a hashtable leading to the position of each node in the heap might be useful to optimise the "pruning" of the heap, when collecting the nodes along a path.
A little analysis
This approach is slightly better than the first one in terms of complexity, but may not lead to the optimal result. In fact, it can even perform quite poorly on some graph configurations. For example, if all nodes have a reward r, except one node T that has r+1 and W(N ↔ T) = C for every node N, but the other edges would be all reachable, then this will only make you collect T and miss every other node. In this particular case, the best solution would have been to ignore T and collect everyone else leading to a reward of (N-1).r instead of only r+1.

Shortest Path Algorithm with Fuel Constraint and Variable Refueling

Suppose you have an undirected weighted graph. You want to find the shortest path from the source to the target node while starting with some initial "fuel". The weight of each edge is equal to the amount of "fuel" that you lose going across the edge. At each node, you can have a predetermined amount of fuel added to your fuel count - this value can be 0. A node can be visited more than once, but the fuel will only be added the first time you arrive at the node. **All nodes can have different amounts of fuel to provide.
This problem could be related to a train travelling from town A to town B. Even though the two are directly connected by a simple track, there is a shortage of coal, so the train does not have enough fuel to make the trip. Instead, it must make the much shorter trip from town A to town C which is known to have enough fuel to cover the trip back to A and then onward to B. So, the shortest path would be the distance from A to C plus the distance from C to A plus the distance from A to B. We assume that fuel cost and distance is equivalent.
I have seen an example where the nodes always fill the "tank" up to its maximum capacity, but I haven't seen an algorithm that handles different amounts of refueling at different nodes. What is an efficient algorithm to tackle this problem?
Unfortunately this problem is NP-hard. Given an instance of traveling salesman path from s to t with decision threshold d (Is there an st-path visiting all vertices of length at most d?), make an instance of this problem as follows. Add a new destination vertex connected to t by a very long edge. Give starting fuel d. Set the length of the new edge and the fuel at each vertex other than the destination so that (1) the total fuel at all vertices is equal to the length of the new edge (2) it is not possible to use the new edge without collecting all of the fuel. It is possible to reach the destination if and only if there is a short traveling salesman path.
Accordingly, algorithms for this problem will resemble those for TSP. Preprocess by constructing a complete graph on the source, target, and vertices with nonzero fuel. The length of each edge is equal to the distance.
If there are sufficiently few special vertices, then exponential-time (O(2^n poly(n))) dynamic programming is possible. For each pair consisting of a subset of vertices (in order of nondecreasing size) and a vertex in that subset, determine the cheapest way to visit all of the subset and end at the specified vertex. Do this efficiently by using the precomputed results for the subset minus the vertex and each possible last waypoint. There's an optimization that prunes the subsolutions that are worse than a known solution, which may help if it's not necessary to use very many waypoints.
Otherwise, the play may be integer programming. Here's one formulation, quite probably improvable. Let x(i, e) be a variable that is 1 if directed edge e is taken as the ith step (counting from the zeroth) else 0. Let f(v) be the fuel available at vertex v. Let y(i) be a variable that is the fuel in hand after i steps. Assume that the total number of steps is T.
minimize sum_i sum_{edges e} cost(e) x(i, e)
subject to
for each i, for each vertex v,
sum_{edges e with head v} x(i, e) - sum_{edges e with tail v} x(i + 1, e) =
-1 if i = 0 and v is the source
1 if i + 1 = T and v is the target
0 otherwise
for each vertex v, sum_i sum_{edges e with head v} x(i, e) <= 1
for each vertex v, sum_i sum_{edges e with tail v} x(i, e) <= 1
y(0) <= initial fuel
for each i,
y(i) >= sum_{edges e} cost(e) x(i, e)
for each i, for each vertex v,
y(i + 1) <= y(i) + sum_{edges e} (-cost(e) + f(head of e)) x(i, e)
for each i, y(i) >= 0
for each edge e, x(e) in {0, 1}
There is no efficient algorithm for this problem. If you take an existing graph G of size n you can give each edge a weight of 1, each node a deposit of 5, and then add a new node that you are trying to travel to connected to each node with a weight of 4 * (n -1). Now the existence of a path from the source to the target node in this graph is equivalent to the existence of a Hamiltonian path in G, which is a known np-complete problem. (See http://en.wikipedia.org/wiki/Hamiltonian_path for details.)
That said, you can do better than a naive recursive solution for most graphs. First do a breadth first search from the target node so that every node's distance to the target is known. Now you can borrow the main idea of Dijkstra's A* search. Do a search of all paths from the source, using a priority queue to always try to grow a path whose current distance + the minimum to the target is at a minimum. And to reduce work you probably also want to discard all paths that have returned to a node that they have previously visited, except with lower fuel. (This will avoid silly paths that travel around loops back and forth as fuel runs out.)
Assuming the fuel consumption as positive weight and the option to add the fuel as negative weight and additionally treating the initial fuel available as another negative weighted edge, you can use Bellman Ford to solve it as single source shortest path.
As per this answer, elsewhere, undirected graphs can be addressed by replacing each edge with two in both directions. The only constraint I'm not sure about is the part where you can only refuel once. This may be naturally addressed, by the the algorithm, but I'm not sure.

Optimum path in a graph to maximize a value

I'm trying to come up with a reasonable algorithm for this problem:
Let's say we have bunch of locations. We know the distances between each pair of locations. Each location also has a point. The goal is to maximize the sum of the points while travelling from a starting location to a destination location without exceeding a given amount of distance.
Here is a simple example:
Starting location: C , Destination: B, Given amount of distance: 45
Solution: C-A-B route with 9 points
I'm just curious if there is some kind of dynamic algorithm for this type of problem. What the would be the best, or rather easiest approach for that problem?
Any help is greatly appreciated.
Edit: You are not allowed to visit the same location many times.
EDIT: Under the newly added restriction that every node can be visited only once, the problem is most definitely NP-hard via reduction to Hamilton path: For a general undirected, unweighted graph, set all edge weights to zero and every vertex weight to 1. Then the maximum reachable score is n iif there is a Hamilton path in the original graph.
So it might be a good idea to look into integer linear programming solvers for instance families that are not constructed specifically to be hard.
The solution below assumes that a vertex can be visited more than once and makes use of the fact that node weights are bounded by a constant.
Let p(x) be the point value for vertex x and w(x,y) be the distance weight of the edge {x,y} or w(x,y) = ∞ if x and y are not adjacent.
If we are allowed to visit a vertex multiple times and if we can assume that p(x) <= C for some constant C, we might get away with the following recurrence: Let f(x,y,P) be the minimum distance we need to get from x to y while collecting P points. We have
f(x,y,P) = ∞ for all P < 0
f(x,x,p(x)) = 0 for all x
f(x,y,P) = MIN(z, w(x, z) + f(z, y, P - p(x)))
We can compute f using dynamic programming. Now we just need to find the largest P such that
f(start, end, P) <= distance upper bound
This P is the solution.
The complexity of this algorithm with a naive implementation is O(n^4 * C). If the graph is sparse, we can get O(n^2 * m * C) by using adjacency lists for the MIN aggregation.

Resources