rudimentary flight trip planner - algorithm

![question based on travel planner][1]
what approach will be best for solving this problem?,any kind of help will be appreciated
The input is the set of flights between various cities. It is given as a file. Each line of the file contains "city1 city2 departure-time arrival-time flight-no. price" This means that there is a flight called "flight-no" (which is a string of the form XY012) from city1 to city2 which leaves city1 at time "departure-time" and arrives city2 at time "arrival-time". Further the price of this flight is "price" which is a poitive integer. All times are given as a string of 4 digits in the 24hr format e.g. 1135, 0245, 2210. Assume that all city names are integers between 1 and a number N (where N is the total number of cities).
Note that there could be multiple flights between two cities (at different times).
The query that you have to answer is: given two cities "A" and "B", times "t1", "t2", where t1 < t2, find the cheapest trip which leaves city "A" after time "t1" and arrives at city "B" before time "t2". A trip is a sequence of flights which starts at A after time t1 and ends at B before time t2. Further, the departure time from any transit (intermediate) city C is at least 30 mins after the arrival at C

You can solve this problem with a graph search algorithm, such as Dijkstra's Algorithm.
The vertices of the graph are tuples of locations and (arrival) times. The edges are a combination of a layover (of at least 30 minutes) and an outgoing flight. The only difficulty is marking the vertices we've visited already (the "closed" list), since arriving in an airport at a given time shouldn't prevent consideration of flights into that airport that arrive earlier. My suggestion is to mark the departing flights we've already considered, rather than marking the airports.
Here's a quick implementation in Python. I assume that you've already parsed the flight data into a dictionary that maps from the departing airport name to a list of 5-tuples containing flight info ((flight_number, cost, destination_airport, departure_time, arrival_time)):
from heapq import heappush, heappop
from datetime import timedelta
def find_cheapest_route(flight_dict, start, start_time, target, target_time):
queue = [] # a min-heap based priority queue
taken_flights = set() # flights that have already been considered
heappush(queue, (0, start, start_time - timedelta(minutes=30), [])) # start state
while queue: # search loop
cost_so_far, location, time, route = heappop(queue) # pop the cheapest route
if location == target and time <= target_time: # see if we've found a solution
return route, cost
earliest_departure = time + timedelta(minutes=30) # minimum layover
for (flight_number, flight_cost, flight_dest, # loop on outgoing flights
flight_departure_time, flight_arrival_time) in flight_dict[location]:
if (flight_departure_time >= earliest_departure and # check flight
flight_arrival_time <= target_time and
flight_number not in taken_flights):
queue.heappush(queue, (cost_so_far + flight_cost, # add it to heap
flight_dest, flight_arrival_time,
route + [flight_number]))
taken_flights.add(flight_number) # and to the set of seen flights
# if we got here, there's no timely route to the destination
return None, None # or raise an exception

If you don't care about efficiency, you can solve the problem like this:
For each "final leg" flight arriving at the destination before t2, determine the departure city of the flight (cityX) and the departure time of the flight (tX). Subtract 30 minutes from the departure time (tX-30). Then recursively find the cheapest trip from start, departing after t1, arriving in cityX before tX-30. Add the cost of that trip to the cost of the final leg to determine the total cost of the trip. The minimum over all those trips is the flight you want.
There is perhaps a more efficient dynamic programming approach, but I might start with the above (which is very easy to code recursively).

Related

minimum number of days to reach destination | graph

Given a graph in which each node represents a city. Some cities are connected to each other through bidirectional roads. The length of each road is also given. Some of the cities have hotels. Given a start city and a destination city and a value K which represents the maximum distance that can be travelled by a person in one day, find the minimum number of days in which the person can reach his destination (or tell if it is impossible for the given K).
(Note : If the distance travelled in one day is exceeding K, the person can rest in the city which has a hotel in it, if there is no hotel in that city this implies you have to choose another path. Next day, the person can start from that city and the distance travelled get reset to 0).
This can be done with Dijkstra:
lets say our dijkstra state is {daysPassed, timeTravelled, vertexId}
initial state is {0,0,startCity} we add it to priority queue (sorted as tuples, min first)
For each state in queue:
try going to all neighboring cities (if timeTravelled + edgeWeigth <= maxTravelTimePerDay), state changes to {daysPassed, timeTravelled + edgeWeight, neighbourId}
try to sleep in current city (if it has a hotel), state changes to {daysPassed + 1, 0, vertexId}
Of course its not profitable to sleep in same hotel 2 times so we can additionally keep a boolean array indicating whether we slept in some hotel and only try to sleep in hotel once (on first time visiting vertex)
if any time during algorithm we reach goal city daysPassed from state is the answer

Minimal car refills on a graph of cities

There's a graph of cities.
n - cities count
m - two-ways roads count
k - distance that car can go after refill
Road i connects cities pi and qi and has length of ri. Between the two cities can be only one road.
A man is going from city u to city v.
There are a gas stations in l cities a1, a2, ..., al.
A car starts with a full tank. If a man gets in a city with a gas station, he can refill (full tank) a car or ignore it.
Return value is a minimal count of refills to get from a city u to city v or -1 if it's impossible.
I tried to do it using Dijkstra algorithm, so I have minimal distance and path. But I have no idea how to get minimal refills count
It is slightly subtle, but the following pseudo-code will do it.
First do a breadth-first search from v to find the distance from every city to the target. This gives us a distance_remaining lookup with distance_remaining[city] being the shortest path (without regards to fillup stations).
To implement we first need a Visit data structure with information about visiting a city on a trip. What fields do we need?
city
fillups
range
last_visit
Next we need a priority queue (just like Dijkstraa) for possible visits to consider. This queue should prioritize visits by the shortest possible overall trip that we might be able to take. Which is to say visit.fillups * max_range + (max_range - visit.range) + distance_remaining[visit.city].
And finally we need a visited[city] data structure saying whether a city is visited. In Dijkstra we only consider a node if it was not yet visited. We need to tweak that to only consider a node if it was not yet visited or was visited with a range shorter than our current one (a car that arrived on full may finish even though the empty one failed).
And now we implement the following logic:
make visit {city: u, fillups: 0, range: max_range, last_visit: None}
add to priority queue the visit we just created
while v = queue.pop():
if v.city == u:
return v.fillups # We could actually find the path at this point!
else if v not in visited or visited[v.city] < v.range:
for each road r from v.city:
if r.length < v.range:
add to queue {city: r.other_city, fillups: v.fillups, range:v.range - r.length, last_visit: v}
if v.city has fillup station:
add to queue {city: v.city, fillups: fillups + 1, range: max_range, last_visit: v}
return -1

Maximum profit earned in Interval

We have to rent our car to customers. We have a list whose each element represent the time at which car will be given, second -> the time at which car will be returned and third -> the profit earned at that lending. So i need to find out the maximum profit that can be earned.
Eg:
( [1,2,20], [3,6,15], [2,8,25], [7,12,18], [13,31,22] )
The maximum profit earned is 75. [1,2] + [3,6] + [7,12] + [13,31].
We can have overlapping intervals. We need to select such case that maximizes our profit.
Assuming you have only one car, then the problem we are solving in Weighted Interval Scheduling
Let us assume we have intervals I0 , I1, I2, ....In-1 and Interval Ii is (si,ti,pi)
Algorithm :
First sort the Intervals on the basis of starting points si.
Create a array for Dynamic Programming, MaxProfit[i] represent the maximum profit you can make from intervals Ii,Ii+1,In-1.Initialise the last value
MaxProfit[n-1] = profit_of_(n-1)
Then using DP we can find the maximum profit as :
a. Either we can ignore the given interval, In this case our maximum profit will be the maximum profit we can gain from the remaining intervals
MaxProfit[i+1]
b. Or we can include this interval, In this case the maximum profit can be written as
profit_of_i + MaxProfit[r]
where r is the next Interval such that sr > ti
So our overall DP becomes
MaxProfit[i] = max{MaxProfit[i+1], profit_of_i + MaxProfit[r] }
Return the value of MaxProfit[0]
use something like dynamic programming.
at first sort with first element.
you have 2 rows, first show most earned if this time is used and another is for most earned if not used.
then you will put each task in the related period of time and see in each time part it is a good choice to have it or not.
take care that if intervals are legal we choose all of them.

Neo4J - Traveling Salesman

I'm trying to solve an augmented TSP problem using a graph database, but I'm struggling. I'm great with SQL, but am a total noob on cypher. I've created a simple graph with cities (nodes) and flights (relationships).
THE SETUP: Travel to 8 different cities (1 city per week, no duplicates) with the lowest total flight cost. I'm trying to solve an optimal path to minimize the cost of the flights, which changes each week.
Here is a file on pastebin containing my nodes & relationships. Just run it against Neo4JShell to insert the data.
I started off using this article as a basis but it doesn't handle the changing distances (or in my case flight costs)
I know this is syntactically terrible/non-executable, but here's what I've done so far to get just two flights;
MATCH (a:CITY)-[F1:FLIGHT{week:1}]->(b:CITY) -[F2:FLIGHT{week:2}]->(c:CITY)
RETURN a,b,c;
But that doesn't run.
Next, I thought I'd just try to find all the cities & flights from week one, but it's not working right either as I get flights where week <> 1 as well as =1
MATCH (n) WHERE (n)-[:FLIGHT { week:1 }]->() RETURN n
Can anyone help out?
PS - I'm not married to using a graph DB to solve this, I've just read about them, and thought it would be well fitted to try it, plus gave me a reason to work with them, but so far, I'm not having much (or any) success.
Maybe this Cypher query will give you some ideas.
MATCH (from:Node {name: "Source node" })
MATCH path = (from)-[:CONNECTED_TO*6]->()
WHERE ALL(n in nodes(path) WHERE 1 = length(filter(m in nodes(path) WHERE m = n)))
AND length(nodes(path)) = 7
RETURN path,
reduce(distance = 0, edge in relationships(path) | distance + edge.distance)
AS totalDistance
ORDER BY totalDistance ASC
LIMIT 1
It does all permutations of available routes which are equal to the number of nodes (for this example it is 7), calculates lengths of all these paths and returns the shortest one.
neo4j may be a fine piece of software, but I wouldn't expect it to be of much help in solving this NP-hard problem. Instead, I would point you to an integer program solver (this one, perhaps, but I can't vouch for it) and suggest that you formulate this problem as an integer program as follows.
For each flight f, we create a 0-1 variable x(f) that is 1 if flight f is taken and 0 if flight f is not taken. The objective is to minimize the total cost of the flights (I'm going to assume that each purchase is an independent decision; if not, then you have some more work to do).
minimize sum_{flights f} cost(f) x(f)
Now we need some constraints. Each week, we purchase exactly one flight.
for all weeks i, sum_{flights f in week i} x(f) = 1
We can be in only one place at a time, so if we fly into city v for week i, then we fly out of city v for week i+1. We express this constraint with a strange but idiomatic linear equation.
for all weeks i, for all cities v,
sum_{flights f in week i to city v} x(f) -
sum_{flights f in week i+1 from city v} x(f) = 0
We can fly into each city at most once. We can fly out of each city at most once. This is how we enforce the constraint of visiting only once.
for all cities v,
sum_{flights f to city v} x(v) <= 1
for all cities v,
sum_{flights f from city v} x(v) <= 1
We're almost done. I'm going to assume at this point that the journey begins and ends in a home city u known ahead of time. For the first week, delete all flights not departing from u. For the last week, delete all flights not arriving at u. The flexibility of integer programming, however, means that it's easy to make other arrangements.

Determine parent, children from pairwise data

I need to determine parent/child relationships from some unusual data.
Flight numbers are marketing creations and they are odd. Flight # 22 by Airline X may refer to a singular trip between X and Y. Flight # 44 from the same airline may actually refer to multiple flights between city pairs. Example:
Flight 44: Dallas - Paris
Flight 44: Dallas - Chicago
Flight 44: Chicago - New York
Flight 44: New York - Paris
Flight 44: Chicago - Paris
Flight 44: Dallas - New York
Reality -- this is the way they work. When I pull the data from the "big list of flight numbers and city pairs" I get those 6 combinations for flight 44. I have passenger counts for each, so if there are 10 people flying Dallas - Paris, I need to take those 10 passengers and add them to the DAL - CHI, CHI - NY, and NY - PAR segments.
From a list of all the segments, I need to figure out "ahhh, this is a flight that goes from Dallas to Paris" --then when I see passenger loads I can increment the city-to-city actual loads accordingly like so:
- Value associated with AD -- > increment segments AB, BC, CD
- value associated with AC --> increment only segments AB, BC
- value associated with AB --> increment only segment AB
etc.
Assume I get a list of values in no order for flight 44 like this: (DAL-CHI, CHI-NYC, NYC-PAR, DAL-NYC, DAL-PAR, CHI-PAR). How do I figure out the parent child structure comparing these 4 values in these 6 combinations?
Formulation
Let a_i -> b_i be the ith entry in your list of pairs for flight 44, i = 1..M.
Let V be the set of all unique a_i and b_i values:
V = {a_i | i = 1..M} U {b_i | i = 1..M}
Let E be the set of all pairs (a_i, b_i):
E = {(a_i, b_i) | i = 1..M}
Then G = (V, E) is a directed acyclic graph where vertices V are cities and directed edges E correspond to entries a_i -> b_i in your list.
Algorithm
What you are looking for is a topological sort of the graph G. The linked Wikipedia page has pseudocode for this algorithm.
This will give you a linear ordering of the cities (in your example: [Dallas, Chicago, New York, Paris]) that is consistent with all of the ordering constraints present in your initial list. If your initial list contains fewer than |V| choose 2 pairs (meaning there is not a full set of constraints) then there will potentially be multiple consistent topological orderings of the cities in your set V.
Note: This is a common-sense analysis, but see Timothy Shields solution where he identified the problem as Topological Sorting problem, thus having known computation complexity & known conditions on uniqueness.
I will try to extract the core of the problem from your answer in order to describe it formally.
In the above example, you actually have four nodes (cities), for brevity denoted as D, P, C, and NY. You have a set of ordered pairs (x, y), which are interpreted as "on that flight, node x precedes node y". Writing this as x<y, we actually have the following:
(for flight 044):
D < P
D < C
C < NY
NY < P
C < P
D < NY
From these constraints, we want to find an ordered tuple (x, y, z, w) such that x < y < z < w and the above constraints hold. We know that the solution is (x=D, y=C, z=NY, w=P).
Note: It might be that in your database, the first element in your set is always the "origin-destination pair" (in our case, D<P). But it does not change much on the analysis which follows.
How to find this ordered tuple programatically? I have relatively fair knowledge of algorithms, but am not aware of a standard method for solving this (other users may help here). I am concerned about the uniqueness of the result. It could be a good unit test of the integrity of your data that you should require that the solution for that ordered tuple is unique, otherwise you might be, subsequently, incrementing the wrong segments.
As we deal with uniqueness issue, I would suggest generating all the permutations of nodes, and displaying all the solutions which are feasible w.r.t the given constraints.
A naive implementation could look like this:
import itertools
nodes = ['D', 'P', 'NY', 'C']
result = [ot
for ot in itertools.permutations(nodes) # ot = ordered tuple
if ot.index('D') < ot.index('P')
if ot.index('D') < ot.index('C')
if ot.index('C') < ot.index('NY')
if ot.index('NY') < ot.index('P')
if ot.index('C') < ot.index('P')
if ot.index('D') < ot.index('NY')
]
print result
# displays: [('D', 'C', 'NY', 'P')]
If the number of nodes is low, this type of "naive" implementation may be sufficient. If the number is higher, I would suggest to implement it in such a way that the constrains are used effectively to prune the solution space (ask me if you would need hints for this).
Construct a list of all cities that are either departures or destinations from your flight list. This gives four
cities:
Dallas
Paris
Chicago
New York
Iterate over the flight list again and count the number of occurances of each destination city:
0 Dallas
3 Paris
1 Chicago
2 New York
Sort the list by the destination count and you have the route:
Dallas -> Chicago -> New York -> Paris
Note: If the destination counts are not contiguous starting with zero (eg. 0, 1, 2, 3...) it points to either an inconsistent or incomplete departure/destination list for that flight.
Okay, take two: here's a function that will take a string such as the one you provided and topologically sort it as per that wikipedia article.
import re
import itertools
def create_nodes(segments):
remaining_segments = re.findall(r'(\w*?)-(\w*?)[,)]', segments)
nodes = []
while len(remaining_segments) > 1:
outgoing, incoming = zip(*remaining_segments)
point = next(node for node in outgoing if node not in incoming)
nodes.append(point)
remaining_segments = [segment for segment in remaining_segments if segment[0] != point]
last_segment = remaining_segments.pop()
nodes.extend(last_segment)
return nodes
Test:
>>> foo = '(DAL-CHI, CHI-NYC, NYC-PAR, DAL-NYC, DAL-PAR, CHI-PAR)'
>>> scratch.create_nodes(foo)
['DAL', 'CHI', 'NYC', 'PAR']
Note that this is not a perfect topological sort function for every use; however, for your specific use case of multiple-stop one-way journeys it should be effective.
Have you looked into using a dictionary with a list store in it.
a dictionary is basically a hash table and you can store a key (the begging and end point ex, AD) and a value (the segments it needs to go through [AB, BC, CD])

Resources