Shortest Path DAG

Master DAG shortest path algorithms, path counting, and topological sorting optimization

Overview

Shortest path problems on DAGs (Directed Acyclic Graphs) have special properties that allow linear O(V+E) time complexity through topological sorting. This chapter explores DAG shortest paths, path counting, critical paths, and other advanced algorithms.

Core Content

1. DAG Shortest Path Algorithm

// DAG shortest path implementation
#include <iostream>
#include <vector>
#include <queue>
#include <climits>
using namespace std;

class DAGShortestPath {
private:
    int n;
    vector<vector<pair<int, long long>>> adj;
    vector<int> indegree;
    vector<long long> dist;
    vector<int> parent;
    
public:
    DAGShortestPath(int vertices) : n(vertices) {
        adj.resize(n);
        indegree.resize(n);
        dist.resize(n);
        parent.resize(n);
    }
    
    void addEdge(int u, int v, long long weight) {
        adj[u].push_back({v, weight});
        indegree[v]++;
    }
    
    // Topological sort + shortest path
    bool shortestPath(int source) {
        // Initialize distances
        fill(dist.begin(), dist.end(), LLONG_MAX);
        fill(parent.begin(), parent.end(), -1);
        dist[source] = 0;
        
        // Topological sorting
        queue<int> q;
        vector<int> tempIndegree = indegree;
        
        for (int i = 0; i < n; i++) {
            if (tempIndegree[i] == 0) {
                q.push(i);
            }
        }
        
        vector<int> topoOrder;
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            topoOrder.push_back(u);
            
            for (auto [v, weight] : adj[u]) {
                tempIndegree[v]--;
                if (tempIndegree[v] == 0) {
                    q.push(v);
                }
            }
        }
        
        // Check for cycles
        if (topoOrder.size() != n) {
            return false; // Cycle exists
        }
        
        // Relax edges in topological order
        for (int u : topoOrder) {
            if (dist[u] != LLONG_MAX) {
                for (auto [v, weight] : adj[u]) {
                    if (dist[u] + weight < dist[v]) {
                        dist[v] = dist[u] + weight;
                        parent[v] = u;
                    }
                }
            }
        }
        
        return true;
    }
    
    // Get shortest distance to target
    long long getDistance(int target) {
        return dist[target];
    }
    
    // Reconstruct shortest path
    vector<int> getPath(int target) {
        vector<int> path;
        int current = target;
        
        while (current != -1) {
            path.push_back(current);
            current = parent[current];
        }
        
        reverse(path.begin(), path.end());
        return path;
    }
    
    // Print all shortest distances
    void printDistances(int source) {
        cout << "From node " << source << " shortest distances:" << endl;
        for (int i = 0; i < n; i++) {
            if (dist[i] == LLONG_MAX) {
                cout << "Node " << i << ": unreachable" << endl;
            } else {
                cout << "Node " << i << ": " << dist[i] << endl;
            }
        }
    }
};

// DAG path counting
class DAGPathCounting {
private:
    int n;
    vector<vector<int>> adj;
    vector<int> indegree;
    vector<long long> pathCount;
    
public:
    DAGPathCounting(int vertices) : n(vertices) {
        adj.resize(n);
        indegree.resize(n);
        pathCount.resize(n);
    }
    
    void addEdge(int u, int v) {
        adj[u].push_back(v);
        indegree[v]++;
    }
    
    // Count paths from source to all nodes
    bool countPaths(int source) {
        fill(pathCount.begin(), pathCount.end(), 0);
        pathCount[source] = 1;
        
        // Topological sorting
        queue<int> q;
        vector<int> tempIndegree = indegree;
        
        for (int i = 0; i < n; i++) {
            if (tempIndegree[i] == 0) {
                q.push(i);
            }
        }
        
        vector<int> topoOrder;
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            topoOrder.push_back(u);
            
            for (int v : adj[u]) {
                tempIndegree[v]--;
                if (tempIndegree[v] == 0) {
                    q.push(v);
                }
            }
        }
        
        if (topoOrder.size() != n) {
            return false;
        }
        
        // Count paths in topological order
        for (int u : topoOrder) {
            for (int v : adj[u]) {
                pathCount[v] += pathCount[u];
            }
        }
        
        return true;
    }
    
    long long getPathCount(int target) {
        return pathCount[target];
    }
    
    void printPathCounts(int source) {
        cout << "From node " << source << " path counts:" << endl;
        for (int i = 0; i < n; i++) {
            cout << "Node " << i << ": " << pathCount[i] << endl;
        }
    }
};

// Critical Path Method (CPM)
class CriticalPathMethod {
private:
    int n;
    vector<vector<pair<int, int>>> adj, radj; // Forward and reverse adjacency lists
    vector<int> indegree, outdegree;
    vector<int> earliestTime, latestTime;
    
public:
    CriticalPathMethod(int vertices) : n(vertices) {
        adj.resize(n);
        radj.resize(n);
        indegree.resize(n);
        outdegree.resize(n);
        earliestTime.resize(n);
        latestTime.resize(n);
    }
    
    void addEdge(int u, int v, int duration) {
        adj[u].push_back({v, duration});
        radj[v].push_back({u, duration});
        indegree[v]++;
        outdegree[u]++;
    }
    
    // Calculate critical path
    bool findCriticalPath() {
        // Calculate earliest start times
        fill(earliestTime.begin(), earliestTime.end(), 0);
        
        queue<int> q;
        vector<int> tempIndegree = indegree;
        
        for (int i = 0; i < n; i++) {
            if (tempIndegree[i] == 0) {
                q.push(i);
            }
        }
        
        vector<int> topoOrder;
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            topoOrder.push_back(u);
            
            for (auto [v, duration] : adj[u]) {
                earliestTime[v] = max(earliestTime[v], earliestTime[u] + duration);
                tempIndegree[v]--;
                if (tempIndegree[v] == 0) {
                    q.push(v);
                }
            }
        }
        
        if (topoOrder.size() != n) {
            return false;
        }
        
        // Calculate latest start times
        int projectDuration = *max_element(earliestTime.begin(), earliestTime.end());
        fill(latestTime.begin(), latestTime.end(), projectDuration);
        
        vector<int> tempOutdegree = outdegree;
        queue<int> rq;
        
        for (int i = 0; i < n; i++) {
            if (tempOutdegree[i] == 0) {
                rq.push(i);
            }
        }
        
        while (!rq.empty()) {
            int v = rq.front();
            rq.pop();
            
            for (auto [u, duration] : radj[v]) {
                latestTime[u] = min(latestTime[u], latestTime[v] - duration);
                tempOutdegree[u]--;
                if (tempOutdegree[u] == 0) {
                    rq.push(u);
                }
            }
        }
        
        return true;
    }
    
    // Get critical activities
    vector<pair<int, int>> getCriticalActivities() {
        vector<pair<int, int>> critical;
        
        for (int u = 0; u < n; u++) {
            for (auto [v, duration] : adj[u]) {
                // Check if critical activity
                if (earliestTime[u] + duration == latestTime[v] && 
                    earliestTime[u] == latestTime[u]) {
                    critical.push_back({u, v});
                }
            }
        }
        
        return critical;
    }
    
    void printResults() {
        cout << "Node timing information:" << endl;
        for (int i = 0; i < n; i++) {
            cout << "Node " << i << ": Earliest=" << earliestTime[i] 
                 << ", Latest=" << latestTime[i] << endl;
        }
        
        vector<pair<int, int>> critical = getCriticalActivities();
        cout << "Critical activities:" << endl;
        for (auto [u, v] : critical) {
            cout << u << " -> " << v << endl;
        }
    }
};

int main() {
    // Test DAG shortest path
    DAGShortestPath dag(6);
    dag.addEdge(0, 1, 5);
    dag.addEdge(0, 2, 3);
    dag.addEdge(1, 3, 6);
    dag.addEdge(1, 2, 2);
    dag.addEdge(2, 4, 4);
    dag.addEdge(2, 5, 2);
    dag.addEdge(2, 3, 7);
    dag.addEdge(3, 4, -1);
    dag.addEdge(4, 5, -2);
    
    if (dag.shortestPath(0)) {
        dag.printDistances(0);
        
        vector<int> path = dag.getPath(5);
        cout << "Path to node 5: ";
        for (int i = 0; i < path.size(); i++) {
            cout << path[i];
            if (i < path.size() - 1) cout << " -> ";
        }
        cout << endl;
    }
    
    // Test path counting
    DAGPathCounting pc(4);
    pc.addEdge(0, 1);
    pc.addEdge(0, 2);
    pc.addEdge(1, 3);
    pc.addEdge(2, 3);
    
    if (pc.countPaths(0)) {
        pc.printPathCounts(0);
    }
    
    // Test critical path
    CriticalPathMethod cpm(6);
    cpm.addEdge(0, 1, 3);
    cpm.addEdge(0, 2, 2);
    cpm.addEdge(1, 3, 2);
    cpm.addEdge(2, 3, 4);
    cpm.addEdge(2, 4, 3);
    cpm.addEdge(3, 5, 2);
    cpm.addEdge(4, 5, 1);
    
    if (cpm.findCriticalPath()) {
        cpm.printResults();
    }
    
    return 0;
}

Problem-Solving Tips

🎯 DAG Recognition

After confirming the graph is a DAG, linear time algorithms can be used, detect cycles through topological sorting.

⚡ Complexity Advantage

DAG shortest path O(V+E) is better than Dijkstra O(V log V + E), suitable for large sparse graphs.

🔧 Implementation Points

Topological sort first then relax edges, can handle negative weights, pay attention to initialization and boundary conditions.

🧮 Application Scenarios

Project scheduling, dependency analysis, critical paths, path counting, and other practical problem modeling and solving.