Leetcode 743. 网络延迟时间(中等) 1514. 概率最大的路径(中等) 1631. 最小体力消耗路径(中等)Dijkstra求两点间的最小权重和
Dajkstra通用模板
用于两结点间最小权重和问题的解决。
Dijkstra 可以理解成一个带 dp table(或者说备忘录)的 BFS 算法,在BFS遍历所有节点的同时,记录并比较权重和。
最后输出Start结点到其他所有节点的最小路径权重和
PS:应用条件:加权有向图,没有负权重边
// 输入一个起点 start,计算从 start 到其他节点的最短距离 vector<int> dijkstra(const vector<vector<vector<int>>>& graph, int k){ // 定义:distTo[i] 的值就是起点 start 到达节点 i 的最短路径权重 vector<int> distTo(graph.size(), INT_MAX); // priority_queue默认为大根堆,但是求最短路径时需要小值,所以返回>,得到的是小根堆,前边的是小值 priority_queue<State> q; // base case,start 到 start 的最短距离就是 0 distTo[k]=0; // 从起点 start 开始进行 BFS q.push(State(k,0)); while(!q.empty()){ State s=q.top(); q.pop(); int curNode=s.id; int curDistFromStart=s.distFromStart; if(distTo[curNode]<curDistFromStart){ continue; } // 将 curNode 的相邻节点装入队列 for(int i=0;i<graph[curNode].size();++i){ int nextNode=graph[curNode][i][0]; int nextDistFromStart=graph[curNode][i][1]+curDistFromStart; // 更新 dp table if(nextDistFromStart<distTo[nextNode]){ q.push(State(nextNode, nextDistFromStart)); distTo[nextNode]=nextDistFromStart; } } } return distTo; } struct State{ // 图节点的 id int id; // 从 start 节点到当前节点的距离 int distFromStart; State(int id,int dist){ this->id=id; this->distFromStart=dist; } // 对于priority_queue,默认是大根堆,如果使用>来判断就得到相反的小根堆 bool operator<(const State& b) const{ return distFromStart>b.distFromStart; } };
题目:
求所有节点都收到信号的时间,你把所谓的传递时间看做距离,实际上就是问你「从节点 k
到其他所有节点的最短路径中,最长的那条最短路径距离是多少」,说白了就是让你算从节点 k
出发到其他所有节点的最短路径,就是标准的 Dijkstra 算法
思路:
最经典的求两点间最小路径权重和的问题。
先构建graph,然后使用Dijkstra算法求得start到各节点的最小权重和
如果其中有INT_MAX,意味着有节点走不到
遍历distTo找到最大值,就是最后到达的节点时间,也就是发送到所有节点的时间
class Solution { public: int networkDelayTime(vector<vector<int>>& times, int n, int k) { // 节点编号是从 1 开始的,所以要一个大小为 n + 1 的邻接表 vector<vector<vector<int>>> graph; graph.resize(n+1); // 构造图 for(int i=0;i<times.size();++i){ int from=times[i][0]; int to=times[i][1]; int weight=times[i][2]; // 邻接表存储图结构,同时存储权重信息 graph[from].push_back({to,weight}); } // 启动 dijkstra 算法计算以节点 k 为起点到其他节点的最短路径 vector<int> distTo = dijkstra(graph,k); int ret=0; for(int i=1;i<n+1;++i){ // 有节点不可达,返回 -1 if(distTo[i]==INT_MAX){ return -1; } ret=max(ret, distTo[i]); } return ret; } // 输入一个起点 start,计算从 start 到其他节点的最短距离 vector<int> dijkstra(const vector<vector<vector<int>>>& graph, int k){ // 定义:distTo[i] 的值就是起点 start 到达节点 i 的最短路径权重 vector<int> distTo(graph.size(), INT_MAX); // priority_queue默认为大根堆,但是求最短路径时需要小值,所以返回>,得到的是小根堆,前边的是小值 priority_queue<State> q; // base case,start 到 start 的最短距离就是 0 distTo[k]=0; // 从起点 start 开始进行 BFS q.push(State(k,0)); while(!q.empty()){ State s=q.top(); q.pop(); int curNode=s.id; int curDistFromStart=s.distFromStart; if(distTo[curNode]<curDistFromStart){ continue; } // 将 curNode 的相邻节点装入队列 for(int i=0;i<graph[curNode].size();++i){ int nextNode=graph[curNode][i][0]; int nextDistFromStart=graph[curNode][i][1]+curDistFromStart; // 更新 dp table if(nextDistFromStart<distTo[nextNode]){ q.push(State(nextNode, nextDistFromStart)); distTo[nextNode]=nextDistFromStart; } } } return distTo; } struct State{ // 图节点的 id int id; // 从 start 节点到当前节点的距离 int distFromStart; State(int id,int dist){ this->id=id; this->distFromStart=dist; } // 对于priority_queue,默认是大根堆,如果使用>来判断就得到相反的小根堆 bool operator<(const State& b) const{ return distFromStart>b.distFromStart; } }; };
题目:
思路:
求路径权重乘积的最大值。优先级队列使用大根堆。
累计的为路径权重乘积
class Solution { public: double maxProbability(int n, vector<vector<int>>& edges, vector<double>& succProb, int start, int end) { vector<vector<vector<double>>> graph; graph.resize(n); // 构造邻接表结构表示图 for(int i=0;i<edges.size();++i){ int from=edges[i][0]; int to=edges[i][1]; double prob=succProb[i]; // 无向图就是双向图;先把 int 统一转成 double,待会再转回来 graph[from].push_back({(double)to,prob}); graph[to].push_back({(double)from,prob}); } return dijkstra(graph,start,end); } double dijkstra(vector<vector<vector<double>>>& graph,int start,int end){ // 定义:probTo[i] 的值就是节点 start 到达节点 i 的最大概率 // dp table 初始化为一个取不到的最小值 vector<double> distToStart(graph.size(),0); // base case,start 到 start 的概率就是 1 distToStart[start]=1; // 优先级队列,probFromStart 较大的排在前面 priority_queue<State> q; // 从起点 start 开始进行 BFS q.push(State(start,1)); while(!q.empty()){ State s=q.top(); q.pop(); int curNode=s.id; double curDistFromStart=s.distFromStart; // 遇到终点提前返回 if(curNode==end){ return curDistFromStart; } if(curDistFromStart<distToStart[curNode]){ // 已经有一条概率更大的路径到达 curNode 节点了 continue; } // 将 curNode 的相邻节点装入队列 for(int i=0;i<graph[curNode].size();++i){ int nextNode=graph[curNode][i][0]; // 看看从 curNode 达到 nextNode 的概率是否会更大 double nextDistFromStart=curDistFromStart*graph[curNode][i][1]; if(nextDistFromStart>distToStart[nextNode]){ q.push(State(nextNode,nextDistFromStart)); distToStart[nextNode]=nextDistFromStart; } } } // 如果到达这里,说明从 start 开始无法到达 end,返回 0 return 0; } struct State{ // 图节点的 id int id; // 从 start 节点到达当前节点的概率 double distFromStart; State(int id, double distFromStart){ this->id=id; this->distFromStart=distFromStart; } // 要求概率最大值,所以使用默认的大根堆 bool operator<(const State& b) const{ return distFromStart<b.distFromStart; } }; };
题目:
思路:
常见的二维矩阵题目,如果让你从左上角走到右下角,比较简单的题一般都会限制你只能向右或向下走,但这道题可没有限制哦,你可以上下左右随便走,只要路径的「体力消耗」最小就行。
如果你把二维数组中每个 (x, y)
坐标看做一个节点,它的上下左右坐标就是相邻节点,它对应的值和相邻坐标对应的值之差的绝对值就是题目说的「体力消耗」,你就可以理解为边的权重。
这样一想,是不是就在让你以左上角坐标为起点,以右下角坐标为终点,计算起点到终点的最短路径?Dijkstra 算法是不是可以做到?
只不过,这道题中评判一条路径是长还是短的标准不再是路径经过的权重总和,而是路径经过的权重最大值。而不是和。
class Solution { public: int minimumEffortPath(vector<vector<int>>& heights) { int m=heights.size(); int n=heights[0].size(); // 构造邻接表结构表示图 vector<vector<vector<vector<int>>>> graph(m,vector<vector<vector<int>>>(n,vector<vector<int>>())); int d[4][2]={{0,-1},{0,1},{-1,0},{1,0}}; for(int i=0;i<m;++i){ for(int j=0;j<n;++j){ for(int k=0;k<4;++k){ int v=i+d[k][0]; int w=j+d[k][1]; if(v>=0&&v<m&&w>=0&&w<n){ int height=heights[v][w]; graph[i][j].push_back({v,w,height}); } } } } return dijkstra(graph, heights); } struct State{ // 矩阵中的一个位置 int x; int y; // 从起点 (0, 0) 到当前位置的最小体力消耗(距离) int distFromStart; State(int x,int y,int distFromStart){ this->x=x; this->y=y; this->distFromStart=distFromStart; } // 求最小值,所以用小根堆 bool operator<(const State& b) const{ return distFromStart>b.distFromStart; } }; // Dijkstra 算法,计算 (0, 0) 到 (m - 1, n - 1) 的最小体力消耗 int dijkstra(vector<vector<vector<vector<int>>>>& graph, vector<vector<int>>& heights){ int m=graph.size(); int n=graph[0].size(); // 定义:从 (0, 0) 到 (i, j) 的最小体力消耗是 effortTo[i][j] vector<vector<int>> distFromStart(m,vector<int>(n,INT_MAX)); // base case,起点到起点的最小消耗就是 0 distFromStart[0][0]=0; // 优先级队列,effortFromStart 较小的排在前面 priority_queue<State> q; q.push(State(0,0,0)); while(!q.empty()){ State s=q.top(); q.pop(); int curX=s.x; int curY=s.y; int curDistFromStart=s.distFromStart; // 到达终点提前结束 if(curX==m-1&&curY==n-1){ return curDistFromStart; } if(curDistFromStart>distFromStart[curX][curY]){ continue; } // 将 (curX, curY) 的相邻坐标装入队列 vector<vector<int>>& v=graph[curX][curY]; for(int i=0;i<v.size();++i){ int nextX=v[i][0]; int nextY=v[i][1]; // 计算从 (curX, curY) 达到 (nextX, nextY) 的消耗 // 比较该路径之前的最大消耗,与next-cur的消耗谁大,取最大值 int nextDistFromStart=max(curDistFromStart, abs(heights[nextX][nextY]-heights[curX][curY])); // 更新 dp table if(nextDistFromStart<distFromStart[nextX][nextY]){ distFromStart[nextX][nextY]=nextDistFromStart; q.push(State(nextX,nextY,nextDistFromStart)); } } } // 正常情况不会达到这个 return return 0; } };
联系方式:emhhbmdfbGlhbmcxOTkxQDEyNi5jb20=