最短路径算法-Dijkstra

 使用 Dijkstra 算法的前提,加权有向图,没有负权重边,求最短路径

用到了优先级队列处理数据 => 贪心思想

其实对于dijkstra算法的理解不能认为就是求最小值的算法 => 最优化算法(最大值也可以)

标准 Dijkstra 算法是计算最短路径的,但你有想过为什么 Dijkstra 算法不允许存在负权重边么?

因为 Dijkstra 计算最短路径的正确性依赖一个前提:路径中每增加一条边,路径的总权重就会增加

这个前提的数学证明大家有兴趣可以自己搜索一下,我这里只说结论,其实你把这个结论反过来也是 OK 的:

如果你想计算最长路径,路径中每增加一条边,路径的总权重就会减少,要是能够满足这个条件,也可以用 Dijkstra 算法。

vector dijkstra(int start, vector>>& graph, int n) {
    vector distTo(n + 1, INT_MAX);
    distTo[start] = 0;
    priority_queue, vector>, greater> > pq;
    pq.emplace(0, start);
    while(!pq.empty()) {
        auto [curDistFromStart, curNodeID] = pq.top();
        pq.pop();
        if(curDistFromStart > distTo[curNodeID]) continue;
        for(auto& [nextNodeID, weight]: graph[curNodeID]) {
            int distToNextNode = distTo[curNodeID] + weight;
            if(distTo[nextNodeID] > distToNextNode) {
                distTo[nextNodeID] = distToNextNode;
                pq.emplace(distToNextNode, nextNodeID);
            }
        }
    }
    return distTo;
}

743. 网络延迟时间 - 力扣(LeetCode)

class Solution {
public:
    int networkDelayTime(vector>& times, int n, int k) {
        vector>> graph(n + 1);
        for(int i = 1; i <= n; i++) {
            graph[i] = vector>();
        }
        for(auto& edge: times) {
            int from = edge[0], to = edge[1], weight = edge[2];
            graph[from].emplace_back(to, weight);
        }
        vector distTo = dijkstra(k, graph, n);
        // 找到最长的那一条最短路径
        int res = 0;
        for(int i = 1; i < distTo.size(); i++) {
            if(distTo[i] == INT_MAX) return -1;
            res = max(res, distTo[i]);
        }
        return res;
    }

private:
    vector dijkstra(int start, vector>>& graph, int n) {
        vector distTo(n + 1, INT_MAX);
        distTo[start] = 0;
        priority_queue, vector>, greater> > pq;
        pq.emplace(0, start);
        while(!pq.empty()) {
            auto [curDistFromStart, curNodeID] = pq.top();
            pq.pop();
            if(curDistFromStart > distTo[curNodeID]) continue;
            for(auto& [nextNodeID, weight]: graph[curNodeID]) {
                int distToNextNode = distTo[curNodeID] + weight;
                if(distTo[nextNodeID] > distToNextNode) {
                    distTo[nextNodeID] = distToNextNode;
                    pq.emplace(distToNextNode, nextNodeID);
                }
            }
        }
        return distTo;
    }
};

1631. 最小体力消耗路径 - 力扣(LeetCode)

这题还有一个难点就是抽象出图

class Solution {
public:
    int minimumEffortPath(vector>& heights) {
        int m = heights.size(), n = heights[0].size();
        // 定义:从(0,0)到(i,j)的最小体力消耗是effortTo[i][j]
        vector> effortTo(m, vector(n, INT_MAX));
        effortTo[0][0] = 0;
        auto tupleCmp = [](const auto& e1, const auto& e2) {
            auto&& [x1, y1, d1] = e1;
            auto&& [x2, y2, d2] = e2;
            return d1 > d2;
        };
        priority_queue, vector>, decltype(tupleCmp) > pq(tupleCmp);
        pq.push(tuple(0, 0, 0));
        while(!pq.empty()) {
            auto [curX, curY, curEffort] = pq.top();
            pq.pop();
            if(curX == m - 1 && curY == n - 1) return curEffort;
            if(curEffort > effortTo[curX][curY]) continue;
            for(auto& neighbor: adjacent(heights, curX, curY)) {
                int nextX = neighbor[0];
                int nextY = neighbor[1];
                int effortToNextNode = max(effortTo[curX][curY], abs(heights[curX][curY] - heights[nextX][nextY]));
                if(effortTo[nextX][nextY] > effortToNextNode) {
                    effortTo[nextX][nextY] = effortToNextNode;
                    pq.push(tuple(nextX, nextY, effortToNextNode));
                }
            }
        }
        return -1;
    }

private:
    vector> dirs{{0, 1}, {1, 0}, {-1, 0}, {0, -1}};
    vector> adjacent(vector>& matrix, int x, int y) {
        int m = matrix.size(), n = matrix[0].size();
        vector> neighbors;
        for(auto& dir: dirs) {
            int nx = x + dir[0];
            int ny = y + dir[1];
            if(nx >= m || nx < 0 || ny >= n || ny < 0) continue;
            neighbors.push_back({nx, ny});
        }
        return neighbors;
    }

};

1514. 概率最大的路径 - 力扣(LeetCode)

这题就是加入一条边,变得越来越小的情况 

class Solution {
public:
    double maxProbability(int n, vector>& edges, vector& succProb, int start, int end) {
        vector>> graph(n);
        for(int i = 0; i < edges.size(); i++) {
            int from = edges[i][0];
            int to = edges[i][1];
            double weight = succProb[i];
            // 无向图 == 双向图
            graph[from].emplace_back(weight, to);
            graph[to].emplace_back(weight, from);
        }
        return dijkstra(start, end, graph);
    }

    double dijkstra(int start, int end, vector>>& graph) {
        int n  = graph.size();
        vector probTo(n, -1);
        probTo[start] = 1;
        priority_queue> pq;
        pq.emplace(1, start);
        while(!pq.empty()) {
            auto [curProbFromStart, curNodeID] = pq.top();
            pq.pop();
            if(curNodeID == end) return curProbFromStart;
            if(curProbFromStart < probTo[curNodeID]) continue;
            for(auto [prob, nextNodeID]: graph[curNodeID]) {
                double probToNextNode = probTo[curNodeID] * prob;
                if(probTo[nextNodeID] < probToNextNode) {
                    probTo[nextNodeID] = probToNextNode;
                    pq.emplace(probToNextNode, nextNodeID);
                }
            }
        }
        return 0.0;
    }
};

你可能感兴趣的:(算法,c++,数据结构)