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