(Dijkstra算法)743.网络延迟时间 | 1514.概率最大的路径 | 882. 细分图中的可到达节点

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];
    }
};

882. 细分图中的可到达节点

给你一个无向图(原始图),图中有 n 个节点,编号从 0 到 n - 1 。你决定将图中的每条边 细分 为一条节点链,每条边之间的新节点数各不相同。

图用由边组成的二维数组 edges 表示,其中 edges[i] = [ui, vi, cnti] 表示原始图中节点 ui 和 vi 之间存在一条边,cnti 是将边 细分 后的新节点总数。注意,cnti == 0 表示边不可细分。

要 细分 边 [ui, vi] ,需要将其替换为 (cnti + 1) 条新边,和 cnti 个新节点。新节点为 x1x2, ..., 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;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值