Optimized Dijkstra Algorithm for Minimum Travel Time in Public Transit Networks
Finding the shortest path in a transportation network where multiple travel modes exist rqeuires careful consideration of all possible transitions. In this problem, a traveler can either walk between adjacent stations or take buses that jump to specific positions.
The walking costs are defined as:
- Moving from station x to x+1 takes
inctime units - Moving from station x to x-1 takes
dectime units
Additionally, there are m buses available, each characterized by:
jump[i]: multiplier for position transformationcost[i]: time required for the journey
When taking bus i from station x, the destination becomes x * jump[i].
Approach
Since the graph structure isn't explicitly known beforehand and the target position can be very large, traditional graph algorithms need modification. A reverse approach using Dijkstra's algorithm proves effective, computing distances from the target back to station 0.
For any position pos, the following transitions are considered:
- Direct walk to station 0: cost =
pos * inc - For each bus type
i:- If
posis divisible byjump[i], we can arrive exactly atposby starting frompos / jump[i] - Otherwise, consider arriving at the nearest lower multiple
floor(pos/jump[i]) * jump[i]and walking forward - Or arrive at the nearest higher multiple and walking backward
- If
A priority queue maintains unvisited positions ordered by minimum distance, while a hash map stores computed distances to avoid recomputation.
typedef pair<long long, int> DistanceNode;
class TransitOptimizer {
public:
int calculateMinimumTime(int destination, int forwardCost, int backwardCost,
vector<int>& multipliers, vector<int>& busCosts) {
priority_queue<DistanceNode, vector<DistanceNode>, greater<DistanceNode>> pq;
unordered_map<int, long long> minDistances;
pq.emplace(0, destination);
while (!pq.empty()) {
auto [currentDistance, currentPosition] = pq.top();
pq.pop();
if (minDistances.count(currentPosition)) {
continue;
}
minDistances[currentPosition] = currentDistance;
if (currentPosition == 0) {
break;
}
// Walk directly to origin
pq.emplace(currentDistance + (long long)forwardCost * currentPosition, 0);
// Consider all bus options
for (int busIndex = 0; busIndex < multipliers.size(); busIndex++) {
int divisor = multipliers[busIndex];
int alignedPosition = currentPosition / divisor * divisor;
if (alignedPosition == currentPosition) {
// Exact arrival at target position
pq.emplace(currentDistance + busCosts[busIndex], currentPosition / divisor);
} else {
// Arrive at floor position and walk forward
pq.emplace(currentDistance + busCosts[busIndex] +
(long long)forwardCost * (currentPosition - alignedPosition),
currentPosition / divisor);
// Arrive at ceiling position and walk backward
pq.emplace(currentDistance + busCosts[busIndex] +
(long long)backwardCost * (alignedPosition + divisor - currentPosition),
currentPosition / divisor + 1);
}
}
}
return minDistances[0] % 1000000007;
}
};
An optimized variant combines the bus ride and walking steps, reducing the number of queue operations:
class OptimizedTransitOptimizer {
public:
int calculateMinimumTime(int destination, int forwardCost, int backwardCost,
vector<int>& multipliers, vector<int>& busCosts) {
priority_queue<DistanceNode, vector<DistanceNode>, greater<DistanceNode>> pq;
unordered_map<int, long long> minDistances;
pq.emplace(0, destination);
while (!pq.empty()) {
auto [currentDistance, currentPosition] = pq.top();
pq.pop();
if (minDistances.count(currentPosition)) {
continue;
}
minDistances[currentPosition] = currentDistance;
if (currentPosition == 0) {
break;
}
pq.emplace(currentDistance + (long long)forwardCost * currentPosition, 0);
for (int busIndex = 0; busIndex < multipliers.size(); busIndex++) {
int divisor = multipliers[busIndex];
int basePosition = currentPosition / divisor * divisor;
// Combined bus ride and forward walk
pq.emplace(currentDistance + busCosts[busIndex] +
(long long)forwardCost * (currentPosition - basePosition),
currentPosition / divisor);
if (basePosition != currentPosition) {
// Combined bus ride and backward walk
pq.emplace(currentDistance + busCosts[busIndex] +
(long long)backwardCost * (basePosition + divisor - currentPosition),
currentPosition / divisor + 1);
}
}
}
return minDistances[0] % 1000000007;
}
};