Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Optimized Dijkstra Algorithm for Minimum Travel Time in Public Transit Networks

Tech 2

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 inc time units
  • Moving from station x to x-1 takes dec time units

Additionally, there are m buses available, each characterized by:

  • jump[i]: multiplier for position transformation
  • cost[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:

  1. Direct walk to station 0: cost = pos * inc
  2. For each bus type i:
    • If pos is divisible by jump[i], we can arrive exactly at pos by starting from pos / 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

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;
    }
};

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.