Shortest Path Algorithm with Fuel Constraint and Variable Refueling - algorithm

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.

Related

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

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]

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.

Maximum weighted path between two vertices in a directed acyclic Graph

Love some guidance on this problem:
G is a directed acyclic graph. You want to move from vertex c to vertex z. Some edges reduce your profit and some increase your profit. How do you get from c to z while maximizing your profit. What is the time complexity?
Thanks!
The problem has an optimal substructure. To find the longest path from vertex c to vertex z, we first need to find the longest path from c to all the predecessors of z. Each problem of these is another smaller subproblem (longest path from c to a specific predecessor).
Lets denote the predecessors of z as u1,u2,...,uk and dist[z] to be the longest path from c to z then dist[z]=max(dist[ui]+w(ui,z))..
Here is an illustration with 3 predecessors omitting the edge set weights:
So to find the longest path to z we first need to find the longest path to its predecessors and take the maximum over (their values plus their edges weights to z).
This requires whenever we visit a vertex u, all of u's predecessors must have been analyzed and computed.
So the question is: for any vertex u, how to make sure that once we set dist[u], dist[u] will never be changed later on? Put it in another way: how to make sure that we have considered all paths from c to u before considering any edge originating at u?
Since the graph is acyclic, we can guarantee this condition by finding a topological sort over the graph. topological sort is like a chain of vertices where all edges point left to right. So if we are at vertex vi then we have considered all paths leading to vi and have the final value of dist[vi].
The time complexity: topological sort takes O(V+E). In the worst case where z is a leaf and all other vertices point to it, we will visit all the graph edges which gives O(V+E).
Let f(u) be the maximum profit you can get going from c to u in your DAG. Then you want to compute f(z). This can be easily computed in linear time using dynamic programming/topological sorting.
Initialize f(u) = -infinity for every u other than c, and f(c) = 0. Then, proceed computing the values of f in some topological order of your DAG. Thus, as the order is topological, for every incoming edge of the node being computed, the other endpoints are calculated, so just pick the maximum possible value for this node, i.e. f(u) = max(f(v) + cost(v, u)) for each incoming edge (v, u).
Its better to use Topological Sorting instead of Bellman Ford since its DAG.
Source:- http://www.utdallas.edu/~sizheng/CS4349.d/l-notes.d/L17.pdf
EDIT:-
G is a DAG with negative edges.
Some edges reduce your profit and some increase your profit
Edges - increase profit - positive value
Edges - decrease profit -
negative value
After TS, for each vertex U in TS order - relax each outgoing edge.
dist[] = {-INF, -INF, ….}
dist[c] = 0 // source
for every vertex u in topological order
if (u == z) break; // dest vertex
for every adjacent vertex v of u
if (dist[v] < (dist[u] + weight(u, v))) // < for longest path = max profit
dist[v] = dist[u] + weight(u, v)
ans = dist[z];

Bipartite selection with associated vertex cost

I think I'm looking for an algorithm that can find a "minimum" "selection" in a bipartite graph. Each vertex has an associated (integer) cost to selecting it. I can only find algorithms that minimise the number of vertices in the selected set, not the cost. I'd previously thought I need a "matching", but actually I just need the subset of vertices that cover every edge...
I don't think a greedy solution can work. Suppose our sets are A, B:
Vertices 1,2,3 are in A and have cost 1.
Vertex 4 is in B and has cost 2.
The solution is to remove the most expensive vertex, 4. A greedy solution that chose based on cost would fail. Similarly, if B had cost 10, we could not choose the most connected vertex greedily.
I thought of a different wording: "Given a bipartite graph where each vertex has an associated cost, find a subset of vertices of minimum cost such that every edge is incident on at least one vertex in your selected subset".
Primal LP:
min sum_v c_v x_v
s.t.
forall e=vw. x_v + x_w >= 1
forall v. x_v >= 0
Dual LP:
max sum_e y_e
s.t.
forall v. sum_{e=vw} y_e <= c_v
forall e. y_e >= 0
Find a min cut where the edges are arcs from A to B with infinite capacity, the vertices in A are sources, and the vertices in B are sinks, with all vertices having capacity equal to their cost. (Equivalently, make a supersource with arcs to A and a supersink with arcs from B.)
Take the As that are on the "sink" side of the cut and the Bs that are on the "source" side. Every edge vw is covered because if neither v nor w belonged to the cover then vw would be residual.
Hat tip I think to Jenő Egerváry.

graph - Shortest path with Vertex Weight

Here is an excise:
In certain graph problems, vertices have can have weights instead of
or in addi- tion to the weights of edges. Let Cv be the cost of vertex
v, and C(x,y) the cost of the edge (x, y). This problem is concerned
with finding the cheapest path between vertices a and b in a graph G.
The cost of a path is the sum of the costs of the edges and vertices
encountered on the path.
(a) Suppose that each edge in the graph has a weight of zero (while
non-edges have a cost of ∞).Assume that Cv =1 for all vertices 1≤v≤n
(i.e.,all vertices have the same cost). Give an efficient algorithm to
find the cheapest path from a to b and its time complexity.
(b) Now suppose that the vertex costs are not constant (but are all
positive) and the edge costs remain as above. Give an efficient
algorithm to find the cheapest path from a to b and its time
complexity.
(c) Now suppose that both the edge and vertex costs are not constant
(but are all positive). Give an efficient algorithm to find the
cheapest path from a to b and its time complexity.
Here is my answer:
(a) use normal BFS;
(b) Use dijkstra’s algorithm, but replace weight with vertex weight;
(c)
Also use dijkstra’s algorithm
If only considering about edge weight, then for the key part of dijkstra's algorithm, we have:
if (distance[y] > distance[v]+weight) {
distance[y] = distance[v]+weight; // weight is between v and y
}
Now, by considering about vertex weight, we have:
if (distance[y] > distance[v] + weight + vertexWeight[y]) {
distance[y] = distance[v] + weight + vertexWeight[y]; // weight is between v and y
}
Am I right?
I guess my answer to (c) is too simple, is it?
You are on the right track, and the solution is very simple.
In both B,C, Reduce the problem to normal dijkstra, which assumes no weights on the vertices.
For this, you will need to define w':E->R, a new weight function for edges.
w'(u,v) = w(u,v) + vertex_weight(v)
in (b) w(u,v) = 0 (or const), and the solution is robust to fit (c) as well!
The idea behind it is using an edge cost you the weight of the edge, and the cost of reaching the target vertice. The cost of the source was already paid, so you disregard it1.
Reducing a problem, instead of changing an algorithm is usually much simpler to use, prove and analyze!.
(1) In this solution you "miss" the weight of the source, so the shortest path from s to t will be: dijkstra(s,t,w') + vertex_weight(s)_ [where dijkstra(s,t,w') is the distance from s to t using out w'
The vertex weight can be removed by slicing every vertex a in two vertices a1 and a2 with an edge from a1 to a2 with the weight of a.
I think you are right for the adaptation of dijkstra’s algorithm.

Resources