743.网络延迟时间
有 n 个网络节点,标记为 1 到 n。
给你一个列表 times,表示信号经过 有向 边的传递时间。 times[i] = (ui, vi, wi),其中 ui 是源节点,vi 是目标节点, wi 是一个信号从源节点传递到目标节点的时间。
现在,从某个节点 K 发出一个信号。需要多久才能使所有节点都收到信号?如果不能使所有节点收到信号,返回 -1 。
示例 1:
输入:times = [[2,1,1],[2,3,1],[3,4,1]], n = 4, k = 2
输出:2
思路:BFS+优先队列+贪心
[Python] BFS和DFS算法(第3讲)—— 从BFS到Dijkstra算法_哔哩哔哩_bilibili
Dijkstra 算法模板及应用 :: labuladong的算法小抄 (gitee.io)
求最短路径,所以优先队列用小根堆
初始化distance数组,距离都置为最大值
class Solution {
public:
int networkDelayTime(vector<vector<int>>& times, int n, int k)
{
vector<vector<pair<int,int>>> graph(n+1);
vector<int> distance(n+1,INT_MAX);//distance[i]表示k到i的最短距离
vector<int> visited(n+1,0);//当i从优先队列中弹出时,visited[i]=1
for(vector<int>& edge:times)//构建邻接表
{
int from=edge[0];
int to=edge[1];
int weight=edge[2];
graph[from].emplace_back(to,weight);
}
//优先队列中存pair类型时,默认以pair.first排序,此处构建优先队列,默认以k到v的距离排序
priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>> q;//小根堆
q.emplace(0,k);//压入源节点k,距离为0
while(!q.empty())
{
pair<int,int> min=q.top();//出一个队列中距离最近的
q.pop();
int dist=min.first;
int v=min.second;
//当前的k到v的距离,比k到v的最短距离大,则不进行下面的操作,再次进入循环,弹出队列中剩余的元素
if(distance[v]<dist)
continue;
distance[v]=dist;//k到v的最短距离
visited[v]=1;//已经得到了k距v的最短距离,所以记visited[i]=1
for(pair<int,int> node:graph[v])//遍历与v相邻的其他结点
{
int to=node.first;
int weight=node.second;
if(!visited[to])//还没有找出k到to的最短路径
{
//k到v的距离dist + v到to的距离weight 比当前的k到to的最短距离还小,就更新
if(distance[to]>weight+dist)
{
q.emplace(weight+dist,to);
distance[to]=weight+dist;
}
}
}
}
int maxDistance=INT_MIN;
for(int i=1;i<distance.size();i++)//遍历distance数组
{
maxDistance=max(maxDistance,distance[i]);//找到k到所有结点的最短距离中最大的那个,即传递时间
if(distance[i]==INT_MAX)//如果还存在INT_MAX,代表k无法到达该结点,返回-1
return -1;
}
return maxDistance;
}
};
1514.概率最大的路径
给你一个由 n 个节点(下标从 0 开始)组成的无向加权图,该图由一个描述边的列表组成,其中 edges[i] = [a, b] 表示连接节点 a 和 b 的一条无向边,且该边遍历成功的概率为 succProb[i] 。
指定两个节点分别作为起点 start 和终点 end ,请你找出从起点到终点成功概率最大的路径,并返回其成功概率。
如果不存在从 start 到 end 的路径,请 返回 0 。只要答案与标准答案的误差不超过 1e-5 ,就会被视作正确答案。
示例 1:
输入:n = 3, edges = [[0,1],[1,2],[0,2]], succProb = [0.5,0.5,0.2], start = 0, end = 2
输出:0.25000
解释:从起点到终点有两条路径,其中一条的成功概率为 0.2 ,而另一条为 0.5 * 0.5 = 0.25
示例 2:
输入:n = 3, edges = [[0,1]], succProb = [0.5], start = 0, end = 2
输出:0.00000
解释:节点 0 和 节点 2 之间不存在路径
思路:迪杰斯特拉算法(bfs+贪心+优先队列)
因为要求概率最大的路径,所以优先队列是大根堆
初始化distance数组,概率都置为0
class Solution {
public:
double maxProbability(int n, vector<vector<int>>& edges, vector<double>& succProb, int start, int end) {
vector<int> visited(n,0);
vector<double> distance(n,0);//初始化起点到所有结点的概率为0
vector<vector<pair<int,double>>> 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(to,weight);
graph[to].emplace_back(from,weight);
}
//大根堆,每次出概率最大的那个
priority_queue<pair<double,int>,vector<pair<double,int>>,less<pair<double,int>>> q;
q.emplace(1,start);//压入起点,起点到起点概率为1
while(!q.empty())
{
pair<double,int> out=q.top();
q.pop();
int v=out.second;
double p=out.first;
if(distance[v]>p)//如果最大概率比p大,就不用执行下面的代码
{
continue;
}
distance[v]=p;
visited[v]=1;//已经得出起点到它的最大概率,visited[v]置为1
for(pair<int,double> node:graph[v])//遍历v的相邻节点
{
int to=node.first;
double weight=node.second;
if(!visited[to])//没有得出起点到它的最大概率
{
if(distance[to]<p*weight)//最大概率比p*weight小,就更新最大概率
{
distance[to]=p*weight;
q.emplace(p*weight,to);
}
}
}
}
return distance[end];
}
};
给你一个无向图(原始图),图中有 n
个节点,编号从 0
到 n - 1
。你决定将图中的每条边 细分 为一条节点链,每条边之间的新节点数各不相同。
图用由边组成的二维数组 edges
表示,其中 edges[i] = [ui, vi, cnti]
表示原始图中节点 ui
和 vi
之间存在一条边,cnti
是将边 细分 后的新节点总数。注意,cnti == 0
表示边不可细分。
要 细分 边 [ui, vi]
,需要将其替换为 (cnti + 1)
条新边,和 cnti
个新节点。新节点为 x1
, x2
, ..., xcnti
,新边为 [ui, x1]
, [x1, x2]
, [x2, x3]
, ..., [xcnti+1, xcnti]
, [xcnti, vi]
。
现在得到一个 新的细分图 ,请你计算从节点 0
出发,可以到达多少个节点?如果节点间距离是 maxMoves
或更少,则视为 可以到达 。
给你原始图和 maxMoves
,返回 新的细分图中从节点 0
出发 可到达的节点数 。
示例 1:
输入:edges = [[0,1,10],[0,2,1],[1,2,2]], maxMoves = 6, n = 3 输出:13 解释:边的细分情况如上图所示。 可以到达的节点已经用黄色标注出来。
示例 2:
输入:edges = [[0,1,4],[1,2,6],[0,2,8],[1,3,1]], maxMoves = 10, n = 4 输出:23
示例 3:
输入:edges = [[1,2,4],[1,4,5],[1,3,1],[2,3,4],[3,4,5]], maxMoves = 17, n = 5 输出:1 解释:节点 0 与图的其余部分没有连通,所以只有节点 0 可以到达。
思路:迪杰斯特拉算法
因为有从起点开始,最大步数的限制,所以:
可到达的结点数=初始结点的可到达数+边上新结点的可到达数
如果从原点到初始结点的距离小于最大步数,则初始结点可达
边(u,v)上新结点的到达数=min(边上新结点的总数,从起点到达u时还能走几步+从起点到达v时还能走几步)。
逐个击破,有理有据!
bool operator >(pair<int,int>& a,pair<int,int>& b)
{
return a.second>b.second;
}
class Solution {
public:
int reachableNodes(vector<vector<int>>& edges, int maxMoves, int n) {
vector<vector<pair<int,int>>> graph(n);
vector<int> distTo(n,INT_MAX);
int ans=0;
for(auto& edge:edges)
{
int from=edge[0];
int to=edge[1];
int weight=edge[2]+1;
graph[from].emplace_back(to,weight);
graph[to].emplace_back(from,weight);
}
//重载大于运算符,因为pq比较pair类型时,默认是比较first
priority_queue<pair<int,int>,vector<pair<int,int>>,greater<>> pq;
pq.push({0,0});
distTo[0]=0;
while(!pq.empty())
{
auto[x,y]=pq.top();
pq.pop();
//这个判断类似于设立visited数组,只要每个结点都从pq里出来过一次,就是true,所有的都出来过后就该结束了
if(y>distTo[x])//当所有的distTo都得到最小值后,pq中仍有一些无用的边,这些东西应该出掉
continue;
for(auto& v:graph[x])
{
int w=v.first;
int weight=distTo[x]+v.second;
if(distTo[w]>weight)
{
distTo[w]=weight;
pq.push({w,weight});
}
}
}
for(int minPath:distTo)//可以到达的旧节点
{
if(minPath<=maxMoves)//只要最短路径比最大移动数小,这个结点就是可以到达的
ans++;
}
for(auto& v:edges)//可以到达的边上的新结点
{
//对于每一条边(u,v),这条边上可达的新结点数=min(uv边长,到了u还能走的结点数+到了v还能走的结点数)
int extra=max(0,maxMoves-distTo[v[0]])+max(0,maxMoves-distTo[v[1]]);
ans+=min(v[2],extra);
}
return ans;
}
};