A traveler in a desert wants to go from one oasis to another until a final destination without taking the risk of dying from thirst (i.e. lack of water). He know the position of all the oases from his starting point to his final destination. The traveler needs to drink one liter of wa for each kilometer made. When he leaves his starting point, he has a bottle full of water. When he reaches an oasis,he has two possibilites :
1 - continue his trip
2 - stop and refill his bottle of water to the max
each oasis, the traveler has to pay (say in euros) to a watchmen the quantity of water lost in each oasis. More precisely, he has to pay an amount equivalent to the squared of the number of liters of water emptied form the bottle when he stops and refills his bottle. We also to take into account the quantity of water emptied at the final destination. The problem now is then to find a solution in order to pay the minimum amount of money
a) Propose a greedy approach. Is it optimal ?
We propose to build a solution based on the dynamic programming paradigm. Your solution must consider the following quantities (note accessible points depend on the value of C)
paid(i) : minimum amount of money paid from the starting point to point i when the traveler empties his bottle at point i - This the wuantity we want to optimize
d(i,j) : number of kilometers between point i and point j
C : the capacity of the bottle
Qustions :
1 - Give the structure of an optimal solution (and if possible prove its optimality)
2 - Give a recursive definition of the optimal value paid
3 - Provide an algorithme for finding the solution in polynomial time. You must provide a complete pseudo-code for all solution. Give the complexity (time and space)
4 - Provide an approach for computing the solution (i.e the trip)
5 - Apply yout algorithme to the following example : the bottle has a capacity of 10 liters, and source points located at 8, 9, 16, 18, 24, 27 kilometers from starting point and such that the final point is at 32 kilometers from the starting point
i would like to do a greedy algorithm and a dynamic algorithm, and i can't. I first tried to do the greedy approach
def _cost(lastStop, d, Cmax, i) :
if i == len(d)-1 :
return (d[i]-lastStop)**2
current = d[i]
next = d[i+1]
price = (Cmax-(lastStop-current))**2
#We have water left
if (next-lastStop) >= 0 :
return min(_cost(lastStop, d, Cmax, i+1), _cost(current, d, Cmax, i+1) + price)
#We don't have enough water we have to stop and refuel
else :
return _cost(current, d, Cmax, i+1) + price
def cost(d, Cmax) :
return _cost(d[0],d,Cmax,0)
d = [8, 9, 16, 18, 24, 27, 32]
Cmax = 10
print(cost(d,Cmax))
Related
I have a restroom that I need to place at some point. I want the restroom's placement to minimize the total distance people have to travel to get there.
So I have x apartments, and each house has n people living in each apartment, so the apartments would be like a_1, a_2, a_3, ... a_x and the number of people in a_1 would be n_1, a_2 would be n_2, etc. No two apartments can be in the same space and each apartment has a positive number of people.
So I know the distance between an apartment a_1 and the proposed bathroom, placed at a, would be |a_1 - a|.
MY WORKING:
I defined a cost function, C(a) = SUM[from i = 1 to x] (n_i)|a_i - a|. I want to find the location a that minimizes this cost function, given two arrays - one for the location of the apartments and one for the number of people in each apartment. I want my algorithm to be in O(n) time.
I was thinking of representing this as a graph and using MSTs or Djikstra's but that would not meet the O(n) runtime. Clearly, there must be something I can do without graphs, but I am unsure.
My understanding of your problem:
You have a once dimensional line with points a1,...,an. Each point has a value n1,....n, and you need to pick a point a that minimizes the cost function
SUM[from i = 1 to x] (n_i)|a_i - a|.
Lets assume our input a1...an is sorted.
Our strategy will be a sweep from left to right, calculating possible a on the way.
Things we will keep track of:
total_n : the total number of people
left_n : the number of people living to the left or at our current position
right_n : the number of people living to the right of our current position
a: our current postition
Calculate:
C(a)
left_n = n1
right_n = total_n - left_n
Now we consider what happens to the sum if we move our restroom to the right 1 step. The people on the left get 1 step further away, but the people on the right get 1 step closer.
We can say that C(a+1) = C(a) +left_n -right_n
If the range an-a1 is fairly small, we can use this and just step through the range using this formula to update the sum. Note that when this sum starts increasing we have gone too far and can safely stop.
However, if the apartments are very far apart we cannot step 1 by 1 unit. We need instead to step apartment by apartment. Note
C(a[i]) = C(a[i-1]) + left_n*(a[i]-a[a-1]) - right_n*(a[i]-a[i-1])
If at any point C(a[i]) > C(a[i-1]) we know that the correct position of the restroom is somewhere between i and i-1.
We can calculate that position, lets call it x.
The sum at x is C(a[i-1]) + left_n*(x-a[i-1]) - right_n*(x-a[i-1]) and we want to minimize this. Note that everything but x is known values.
We can simplify to
f(x) = C(a[i-1]) + left_n*x-left_n*a[i-1]) - right_n*x-left_n*a[i-1])
Constant terms cannot affect our decision so we are actually looking to minize
f(x) = x*(left_n-right_n)
We see that if left_n < right_n we want the restroom to be at i+1, but if left_n > right_n we want the restroom to be at i.
We need to at most do this calculation at each apartment, so the running time is O(n).
Question:
Given a tree with N nodes.
Each edges of the tree contains:
D : the length of the edge
T : the gold needed to pay to go through that edge (the gold should be paid before going through the edge)
When moving through an edge, if you're carrying X golds, you will need X*D fuel.
There are 2 types of queries:
u, v: find the fuel needed to transfer G golds from u to v (G is fixed among all queries)
u, v, x: update T of edge {u,v} to x ({u, v} is guaranteed to be in the tree)
Constraints:
2 ≤ N ≤ 100.000
1 ≤ Q ≤ 100.000
1 ≤ Ai, Bi ≤ N
1 ≤ D, T, G ≤ 10^9
Example:
N = 6, G = 2
Take queries 1 with u = 3 and v = 6 for example. First, you start at 3 with 11 golds , pay 2, having 9, and go to node 2 with 9*1 = 9 fuel. Next, we pay 3 gold, having 6, and go to node 4 with 6*2 = 12 fuel. Finally, we pay 4, having 2 gold, and go to node 6 with 2*1 = 2 fuel. So the fuel needed would be 9 + 12 + 2 = 23.
So the answer to query: u = 3, v = 6 would be 23
The second query is just updating T of the edge so I think there's no need for explanation.
My take
I was only able to solve the problem in O(N*Q). Since it's a tree, there's only 1 path from u to v, so for each query, I do a DFS to find the fuel needed to go from u to v. Here's the code for that subtask: https://ideone.com/SyINTQ
For some special cases that all T are 0. We just need to find the length from u to v and multiply it by G. The length from u to v can be easily found using a distance array and LCA. I think this could be a hint for the proper solution.
Is there a way to do the queries in logN or less?
P/S: Please comment if anything needs to be clarified, and sorry for my bad English.
This answer will explain my matrix group comment in detail and then
sketch the standard data structures needed to make it work.
Let’s suppose that we’re carrying Gold and have burned Fuel so far.
If we traverse an edge with parameters Distance, Toll, then the
effect is
Gold -= Toll
Fuel += Gold * Distance,
or as a functional program,
Gold' = Gold - Toll
Fuel' = Fuel + Gold' * Distance.
= Fuel + Gold * Distance - Toll * Distance.
The latter code fragment defines what mathematicians call an action:
each Distance, Toll gives rise to a function from Gold, Fuel to
Gold, Fuel.
Now, whenever we have two functions from a domain to that same domain,
we can compose them (apply one after the other):
Gold' = Gold - Toll1
Fuel' = Fuel + Gold' * Distance1,
Gold'' = Gold' - Toll2
Fuel'' = Fuel' + Gold'' * Distance2.
The point of this math is that we can expand the definitions:
Gold'' = Gold - Toll1 - Toll2
= Gold - (Toll1 + Toll2),
Fuel'' = Fuel' + (Gold - (Toll1 + Toll2)) * Distance2
= Fuel + (Gold - Toll1) * Distance1 + (Gold - (Toll1 + Toll2)) * Distance2
= Fuel + Gold * (Distance1 + Distance2) - (Toll1 * Distance1 + (Toll1 + Toll2 ) * Distance2).
I’ve tried to express Fuel'' in the same form as before: the
composition has “Distance” Distance1 + Distance2 and “Toll”
Toll1 + Toll2, but the last term doesn’t fit the pattern. What we can
do, however, is add another parameter, FuelSaved and define it to be
Toll * Distance for each of the input edges. The generalized update
rule is
Gold' = Gold - Toll
Fuel' = Fuel + Gold * Distance - FuelSaved.
I’ll let you work out the generalized composition rule for
Distance1, Toll1, FuelSaved1 and Distance2, Toll2, FuelSaved2.
Suffice it to say, we can embed Gold, Fuel as a column vector
{1, Gold, Fuel}, and parameters Distance, Toll, FuelSaved as a unit
lower triangular matrix
{{1, 0, 0}, {-Toll, 1, 0}, {-FuelSaved, Distance, 1}}. Then
composition is matrix multiplication.
Now, so far we only have a semigroup. I could take it from here with
data structures, but they’re more complicated when we don’t have an
analog of subtraction (for intuition, compare the problems of finding
the sum of each length-k window in an array with finding the max).
Happily, there is a useful notion of undoing a traversal here (inverse).
We can derive it by solving for Gold, Fuel from Gold', Fuel':
Gold = Gold' + Toll
Fuel = Fuel' - Gold * Distance + FuelSaved,
Fuel = Fuel' + Gold' * (-Distance) - (-FuelSaved - Toll * Distance)
and reading off the inverse parameters.
I promised a sketch of the data structures, so here we are. Root the
tree anywhere. It suffices to be able to
Given nodes u and v, query the leafmost common ancestor of u and v;
Given a node u, query the parameters to get from u to the root;
Given a node v, query the parameters to get from the root to v;
Update the toll on an edge.
Then to answer a query u, v, we query their leafmost common ancestor w
and return the fuel cost of the composition (u to root) (w to root)⁻¹
(root to w)⁻¹ (root to v) where ⁻¹ means “take the inverse”.
The full-on sledgehammer approach here is to implement dynamic trees,
which will do all of these
things in amortized logarithmic
time per operation. But we don’t need dynamic topology updates and can
probably afford an extra log factor, so a set of more easily digestable
pieces would be leafmost common ancestors, heavy path decomposition, and
segment trees (one per path; Fenwick is potentially another option, but
I’m not sure what complications a noncommutative operation might
create).
I told in the comments that a Dijkstra algorithm was necessary, but thinking better the DFS is really enough because there is only one path for each pair of vertices, we will always need to go from the starting point to the endpoint.
Using a priority queue instead of a stack would only change the order that the graph is explored, but in the worst case it would still visit all the vertices.
Using a queue instead of a stack would make the algorithm a breadth first search, again would only change the order in which the graph is explored.
Assuming that the number of nodes in a given distance increases exponentially with the threshold. An improvement for the typical case could be achieved by doing two searches and meeting in the middle. But only a constant factor.
So I think it is better to go with the simple solution, implementing this in C/C++ will result in a program dozens of times faster.
Solution
Prepare adjacency lists, and also makes the graph undirected
from collections import defaultdict
def process_edges(rows):
edges = defaultdict(list)
for u,v,D,T in rows:
edges[u].append((v,(D,T)))
edges[v].append((u,(D,T)))
return edges
It is interesting to do the search backwards because the amount of gold is fixed at the destination, and unknown at the origin, then we can calculate the exact amount of gold and fuel required for each node going backwards.
Of course you can remove the print statement I left there
def dfs(edges, a, b, G):
Q = [((0,G),b)]
visited = set()
while len(Q) != 0:
((Fu,Gu), current_vertex) = Q.pop()
visited.add(current_vertex)
for neighbor,(D,T) in edges[current_vertex]:
if neighbor in visited:
continue; # avoid going backwards
Gv = Gu + T # add the tax of the edge to the gold budget
Fv = Fu + Gv * D # compute the required fuel
print(neighbor, (Fv, Gv))
if neighbor == a:
return (Fv, Gv)
Q.append(((Fv,Gv), neighbor))
Running your example
edges = process_edges([
[6,4,1,4],
[5,4,2,2],
[4,2,2,3],
[3,2,1,2],
[1,2,2,1]
])
dfs(edges,3,6,2)
Will print:
4 (6, 6)
5 (22, 8)
2 (24, 9)
3 (35, 11)
and return (35, 11). It means that for rounte from 3 to 6, it requires 11 gold, and 35 is the fuel used.
I have to implement an algorithm that solves the Towers of Hanoi game for k pods and d rings in a limited number of moves (let's say 4 pods, 10 rings, 50 moves for example) using Bellman dynamic programming equation (if the problem is solvable of course).
Now, I understand the logic behind the equation:
where V^T is the objective function at time T, a^0 is the action at time 0, x^0 is the starting configuration, H_0 is cumulative gain f(x^0, a^0)=x^1.
The cardinality of the state space is $k^d$ and I get that a good representation for a state is a number in base k: d digits that can go from 0 to k-1. Each digit represents a ring and the digit can go from 0 to k-1, that are the labels of the k rings.
I want to minimize the number of moves for going from the initial configuration (10 rings on the first pod) to the end one (10 rings on the last pod).
What I don't get is: how do I write my objective function?
The first you need to do is choose a reward function H_t(s,a) which will define you goal. Once this function is chosen, the (optimal) value function is defined and all you have to do is compute it.
The idea of dynamic programming for the Bellman equation is that you should compute V_t(s) bottom-up: you start with t=T, then t=T-1 and so on until t=0.
The initial case is simply given by:
V_T(s) = 0, ∀s
You can compute V_{T-1}(x) ∀x from V_T:
V_{T-1}(x) = max_a [ H_{T-1}(x,a) ]
Then you can compute V_{T-2}(x) ∀s from V_{T-1}:
V_{T-2}(x) = max_a [ H_{T-2}(x,a) + V_{T-1}(f(x,a)) ]
And you keep on computing V_{t-1}(x) ∀s from V_{t}:
V_{t-1}(x) = max_a [ H_{t-1}(x,a) + V_{t}(f(x,a)) ]
until you reach V_0.
Which gives the algorithm:
forall x:
V[T](x) ← 0
for t from T-1 to 0:
forall x:
V[t](x) ← max_a { H[t](x,a) + V[t-1](f(x,a)) }
What actually was requested was this:
def k_hanoi(npods,nrings):
if nrings == 1 and npods > 1: #one remaining ring: just one move
return 1
if npods == 3:
return 2**nrings - 1 #optimal solution with 3 pods take 2^d -1 moves
if npods > 3 and nrings > 0:
sol = []
for pivot in xrange(1, nrings): #loop on all possible pivots
sol.append(2*k_hanoi(npods, pivot)+k_hanoi(npods-1, nrings-pivot))
return min(sol) #minimization on pivot
k = 4
d = 10
print k_hanoi(k, d)
I think it is the Frame algorithm, with optimization on the pivot chosen to divide the disks in two subgroups. I also think someone demonstrated this is optimal for 4 pegs (in 2014 or something like that? Not sure btw) and conjectured to be optimal for more than 4 pegs. The limitation on the number of moves can be implemented easily.
The value function in this case was the number of steps needed to go from the initial configuration to the ending one and it needed be minimized. Thank you all for the contribution.
I came across this problem quite recently.
Suppose there are n points on x-axis, x[0],x[1] .. x[n-1].
Let the weight associated with each of these points be w[0],w[1] .. w[n-1].
Starting from any point between 0 to n-1, the objective is to cover all the points such that the sum of w[i]*d[i] is minimized where d[i] is the distance covered to reach the ith point from the starting point.
Example:
Suppose the points are: 1 5 10 20 40
Suppose the weights are: 1 2 10 50 13
If I choose to start at point 10 and choose to move to point 20 then to 5 then to 40 and then finally to 1, then the weighted sum will become 10*0+50*(10)+2*(10+15)+13*(10+15+35)+1*(10+15+35+39).
I have tried to solve it using greedy approach by starting off from the point which has maximum associated weight and move to second maximum weight point and so on. But the algorithm does not work. Can someone give me pointers about the approach which must be taken to solve this problem?
There's a very important fact that leads to a polynomial time algorithm:
Since the points are located on some axis, they generate a path graph, which means that for every 3 vertices v1,v2,v3, if v2 is between v1 and v3, then the distance between v1 and v3 equals the distance between v1 and v2 plus the distance between v2 and v3. therefor if for example we start at v1, the obj. function value of a path that goes first to v2 and then to v3 will always be less than the value of the path that first goes to v3 and then back to v2 because:
value of the first path = w[2]*D(v1,v2)+W[3]*(D(v1,v2)+D(v2,v3))
value of the second path = w[3]*D(v1,v3)+W[2]*((v1,v3)+D(v3,v2)) = w[3]*D(v1,v2)+w[3]*D(v2,v3)+w[2]*(D(v1,v2)+2*D(v3,v2))
If we subtract the first path value from the second, we are left with w[2]*2*D(v3,v2) which is equal to or greater than 0 unless you consider negative weights.
All this means that if we are located at a certain point, there are always only 2 options we should consider: going to closest unvisited point on the left or the closest unvisited point on the right.
This is very significant as it leaves us with 2^n possible paths rather than n! possible paths (like in the Travelling Salesman Problem).
Solving the TSP/minimum weight hamiltonian path on path graphs can be done in polynomial time using dynamic programming, you should apply the exact same method but modify the way you calculated the objective function.
Since you don't know the starting vertex, you'll have to run this algorithm n time, each time starting from a different vertex, which means the running time will be multiplied by n.
Maybe you should elaborate what you mean that the algorithm "does not work". The basic idea of the greedy approach that you described seems feasible for me. Do you mean that the greedy approach will not necessarily find the optimal solution? As it was pointed out in the comments, this might be an NP-complete problem - although, to be sure, one would have to analyze it further: Some dynamic programming, and maybe some prefix sums for the distance computations could lead to a polynomial time solution as well.
I quickly implemented the greedy solution in Java (hopefully I understood everything correctly...)
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class MinWeightSum
{
public static void main(String[] args)
{
double x[] = { 1, 5, 10, 20, 40 };
double w[] = { 1, 2, 10, 50, 13 };
List<Integer> givenIndices = Arrays.asList(2, 3, 1, 4, 0);
Path path = createPath(x, w, givenIndices);
System.out.println("Initial result "+path.sum);
List<Integer> sortedWeightIndices =
computeSortedWeightIndices(w);
Path greedyPath = createPath(x, w, sortedWeightIndices);
System.out.println("Greedy result "+greedyPath.sum);
System.out.println("For "+sortedWeightIndices+" sum "+greedyPath.sum);
}
private static Path createPath(
double x[], double w[], List<Integer> indices)
{
Path path = new Path(x, w);
for (Integer i : indices)
{
path.append(i);
}
return path;
}
private static List<Integer> computeSortedWeightIndices(final double w[])
{
List<Integer> indices = new ArrayList<Integer>();
for (int i=0; i<w.length; i++)
{
indices.add(i);
}
Collections.sort(indices, new Comparator<Integer>()
{
#Override
public int compare(Integer i0, Integer i1)
{
return Double.compare(w[i1], w[i0]);
}
});
return indices;
}
static class Path
{
double x[];
double w[];
int prevIndex = -1;
double distance;
double sum;
Path(double x[], double w[])
{
this.x = x;
this.w = w;
}
void append(int index)
{
if (prevIndex != -1)
{
distance += Math.abs(x[prevIndex]-x[index]);
}
sum += w[index] * distance;
prevIndex = index;
}
}
}
The sequence of indices that you described in the example yields the solution
For [2, 3, 1, 4, 0] sum 1429.0
The greedy approach that you described gives
For [3, 4, 2, 1, 0] sum 929.0
The best solution is
For [3, 2, 4, 1, 0] sum 849.0
which I found by checking all permutations of indices (This is not feasible for larger n, of course)
Suppose you are part way through a solution and have traveled for distance D so far. If you go a further distance x and see a point with weight w it costs you (D + x)w. If you go a further distance y and see a point with weight v it costs you (D + x + y)v.. If you sum all of this up there is a component that depends on the path you take after the distance D: xw + xv + yv+..., and there is a component that depends on distance D and the sum of the weights of the points that you need to carry: D (v + w + ...). But the component that depends on distance D does not depend on anything else except the sum of the weights of the points you need to visit, so it is fixed, in the sense that it is the same regardless of the path you take after going distance D.
It always make sense to visit points we pass as we visit them, so the best path will start off with a single point (possibly at the edge of the set of points to be visited and possibly in the centre) and then expand this to an interval of visited points, and then expand this to visit all the points. To pre-calculate the relative costs of visiting all points outside the interval we only need to know the current position and the size of the interval, not the distance travelled so far.
So an expensive but polynomial dynamic programming approach has as the state the current position (which must be one of the points) the position of the first, if any, unvisited point to the left of the current position, and the position, if any, of the first unvisited point to the right of the current point. There are at most two points we should consider visiting next - the point to the right of the current point and the point to the left of the current point. We can work out the cost of these two alternatives by looking at pre-computed costs for states with fewer points left, and store the best result as the best possible cost from this point. We could compute these costs under the fiction that D=0 at the time we reach the current point. When we look up stored costs they are also stored under this assumption (but with D=0 at their current point, not our current point), but we know the sum of the weights of points left at that stage, so we can add to the stored cost that sum of weights times the distance between our current point and the point we are looking up costs for to compensate for this.
That gives cost O(n^3), because you are building a table with O(n^3) cells, with each cell the product of a relatively simple process. However, because it never makes sense to pass cells without visiting them, the current point must be next to one of the two points at either end of the interval, so we need consider only O(n^2) possibilities, which cuts the cost down to O(n^2). A zig-zag path such as (0, 1, -1, 2, -2, 3, -3, 4, -4...) might be the best solution for suitably bizarre weights, but it is still the case, even for instance when going from -2 to 3, that -2 to is the closest point not yet taken between the two points 3 and -3.
I have put an attempted java implementation at http://www.mcdowella.demon.co.uk/Plumber.java. The test harness checks this DP version against a (slow) almost exhaustive version for a number of randomly generated test cases of length up to and including 12. It still may not be completely bug-free, but hopefully it will fill in the details.
You want to get from town A to B being m miles apart using some number
of cabs. Somewhere between the towns (or in one of them) there's a cab
base (each cab starts its journey from the base). Each cab has fuel
for some miles of travel (none of the cabs has to return to the base).
Determine if your travel from A to B is possible.
INPUT:
Integers m,d,n (1<=d<=m<=10^8, 1<=n<=500,000) meaning (in that
order): distance from A to B, distance from A to cab base, number of
cabs. After that, n integers meaning that i-th cab has fuel for x_i-th
miles of travel.
OUTPUT:
One integer: a minimum number of cabs you'll have to use to
get from A to B or 0 if it's impossible.
I tried tackling it with what first stroke my mind and (not surprisingly) it wasn't that good of an idea. Namely, what I did was:
sort(ALL(cabs));
reverse(ALL(cabs));
for(int i = 0; i < n; ++i)
{
toPosition = position <= d ? d-position : position - d;
if(cabs[0] <= toPosition) {printf("0"); return 0;}
position += (cabs[0]-toPosition);
cabs.erase(cabs.begin());
++solution;
if(position >= m) {printf("%lld", solution); return 0;}
}
Now, toPosition is the distance from the base to the current position we're in. Then, we take the cab with the fullest gas tank from the base (if there's none such that it could get us closer to town B, there's no solution). We change our position accordingly (to the max of given cab's capacities), remove the cab and just do it until we're in town B.
I now know that the solution is wrong. I even found some tests where it fails. However, I can't quite get why it does. So for example for test
14 4 2
10 8
It outputs 0 while it should output 2. I know that it's because it wants to go 10->8 while the correct order here is 8 -> 10. Now here's where the problem arises:
Why does the order matter here in this problem? If we have to cover all the distance from A to B anyway and until we're in or after the base location, we have to backtrack with each cab, why not use the ones which would do the backtracking mission the fastest?
Imagine you had 3 cabs, where only 1 has enough fuel to get to A, and it can get you 1 mile short of the base; one has enough fuel to get you from the base to B; and the last has enough fuel for a 2 mile trip. Clearly, you have to use the "smallest" cab second; if you used the other, you'd be just shy of B, with a cab that can't reach you, much less finish the job.
So, yeah, the order does matter.
d: distance to cab base
m: distance from A to B
int[] cabMilliage: the miles that each cab can make
I figured one optimization strategy that you could use:
Start to construct your path 'backwards', from the last cab to the first.
To cover the distance from the cab base to B, you will use only one cab, there is no way to use more than one, so start by selecting this one.
distBaseToB = m - d;
Select the minimum cabMilliage[i] >= distBaseToB
calculate the point where this cab pick you up
pickUpPoint[0] = d - (cabMilliage[i] - distBaseToB)/2
now, continue with a loop, selecting the minimum cabMilliage[] that can pick you up earlier in the path and bring you to pickUpPoint[0]
until you reach A (or run out of cabs that can pick you up at that point of the road because they don't have enough gas.)