迪杰斯特拉算法刷题笔记

理论基础:

求有权图中起点到某一点的最短路径问题

minDist数组:记录源点到某一节点的最短路程

三部曲:

(1)找出距离源点相距最近的点。

(2)标记该点为已访问过。

(3)更新该点相连的点的mindist:距离远点的最近路程。

这里跟prim算法不一样的地方就是第一步,prim算法要找的是距离生成树最近的节点,而迪杰斯特拉算法找的是距离源点最近的节点。

卡码网-47:参加科学大会:

题目:

题目描述

小明是一位科学家,他需要参加一场重要的国际科学大会,以展示自己的最新研究成果。

小明的起点是第一个车站,终点是最后一个车站。然而,途中的各个车站之间的道路状况、交通拥堵程度以及可能的自然因素(如天气变化)等不同,这些因素都会影响每条路径的通行时间。

小明希望能选择一条花费时间最少的路线,以确保他能够尽快到达目的地。

输入描述

第一行包含两个正整数,第一个正整数 N 表示一共有 N 个公共汽车站,第二个正整数 M 表示有 M 条公路。 

接下来为 M 行,每行包括三个整数,S、E 和 V,代表了从 S 车站可以单向直达 E 车站,并且需要花费 V 单位的时间。

输出描述

输出一个整数,代表小明从起点到终点所花费的最小时间。

笔记:

这是一道模板题,再将题目数据存入邻接矩阵grid后我们就可以开始使用迪杰斯特拉算法三部曲进行解题。首先是由于起始点到原点的距离为0,所以我们先将mindist[1]置为0,接下来我们遍历图中所有的点:

(1)找出距离源点最近的节点:

在第一层循环我们需要将到源点最近距离minval设置为INT_MAX,接着遍历所有的点,找出未被访问过的点以及到源点的距离小于初始值minval的,遍历完这n个点之后我们就可以得出距离远点最近的节点。

(2)将该点置为已访问:

也就是将visited数组中的值置为true即可。

(3)更新与该节点相连节点的mindist

这里我们需要注意更新的前提是三个条件:1、当前节点为未访问状态 2、当前节点与该节点相连:grid[cur][v] != INT_MAX 3、当前节点通过该节点到达源点的距离小于自身的mindist。

#include<bits/stdc++.h>
using namespace std;

int main(){
    int n, m;
    cin >> n >> m;
    vector<vector<int>> grid(n + 1, vector<int>(n + 1, INT_MAX));
    vector<bool> visited(n + 1, false);
    vector<int> mindist(n + 1, INT_MAX);
    // 建立邻接矩阵:
    for(int i = 1; i <= m; i++){
        int s,e,v;
        cin >> s >> e >> v;
        grid[s][e] = v;
    }
    mindist[1] = 0;
    for(int i = 1; i <= n; i++){
        int cur;
        int minval = INT_MAX;
        // 第一步:找到距离源点最近的点:
        for(int v = 1; v <= n; v++){
            if(!visited[v] && mindist[v] < minval){
                minval = mindist[v];
                cur = v;
            }
        }
        // 第二步:标记cur为已访问过
        visited[cur] = true;
        // 第三步:更新非生成树节点到源点的最小距离mindist:
        for(int v = 1; v <= n; v++){
            // 更新的条件:(1)未访问过(2)与生成树有连接-> 与当前距离原点最近的点有连接 -> 可能需要更新(3)当前点的距离源点的距离要大于通过当前最近点到达源点的距离:
            if(!visited[v] && grid[cur][v] != INT_MAX && mindist[cur] + grid[cur][v] < mindist[v]){
            // grid[cur][j] < mindist[j] :mindist在这里不再是到生成树的最小距离而是到源点的最小距离就不能再使用上面的语句了
                mindist[v] = mindist[cur] + grid[cur][v];
            }
        }
    }
    if(mindist[n] == INT_MAX)   cout << -1 << endl;
    else    cout << mindist[n] << endl;
    
    
    return 0;
}

Leetcode - 746:网络延迟时间:

题目:

有 n 个网络节点,标记为 1 到 n

给你一个列表 times,表示信号经过 有向 边的传递时间。 times[i] = (ui, vi, wi),其中 ui 是源节点,vi 是目标节点, wi 是一个信号从源节点传递到目标节点的时间。

现在,从某个节点 K 发出一个信号。需要多久才能使所有节点都收到信号?如果不能使所有节点收到信号,返回 -1 。

笔记:

这道题的思路是:从给定的起点k出发,找出到达所以其他点的最短路径,然后注意比较,最大的那个时间就是所有节点都收到信号的时间。

朴素版:
class Solution {
public:
    int networkDelayTime(vector<vector<int>>& times, int n, int k) {
        vector<int> mindist(n + 1, INT_MAX);
        vector<bool> visited(n + 1, false);
        mindist[k] = 0;
        int m = times.size();
        // 建立邻接矩阵grid:
        vector<vector<int>> grid(n + 1, vector<int>(n + 1, INT_MAX));
        for(int i = 0; i < m; i++){
            grid[times[i][0]][times[i][1]] = times[i][2];
        }
        for(int i = 1; i <= n; i++){
            int minval = INT_MAX;
            int cur;
            // 寻找距离源点最近的点:
            for(int i = 1; i <= n; i++){
                if(!visited[i] && mindist[i] < minval){
                    cur = i;
                    minval = mindist[i];
                }
            }
            // 标记该点为已访问状态:
            visited[cur] = true;
            // 更新非生成树点的最短距离
            for(int i = 1; i <= n; i++){
                if(!visited[i] && grid[cur][i] != INT_MAX && mindist[cur] + grid[cur][i] < mindist[i]){
                    mindist[i] = mindist[cur] + grid[cur][i];
                }
            }
        }
            int res = 0;
            int index;
            for(int i = 1; i <= n; i++){
                if(mindist[i] == INT_MAX)    return -1;
                if(mindist[i] > res){
                    res = mindist[i];
                }
            }
            return res;
        }
};
堆优化版:
class Solution {
public:
    struct compare{
        bool operator() (const pair<int, int>& a, const pair<int, int>& b){
            return a.second > b.second;
        }
    };
    int networkDelayTime(vector<vector<int>>& times, int n, int k) {
        vector<int> mindist(n + 1, INT_MAX);
        vector<bool> visited(n + 1, false);
        priority_queue<pair<int, int>, vector<pair<int, int>>, compare> pq;
        vector<vector<int>> grid(n + 1, vector<int>(n + 1, INT_MAX));
        for(int i = 0; i < times.size(); i++){
            grid[times[i][0]][times[i][1]] = times[i][2];
        }
        mindist[k] = 0;
        pq.push(make_pair(k, 0));
        while(!pq.empty()){
            pair<int, int> cur = pq.top();
            pq.pop();
            int minval = INT_MAX;
            if(visited[cur.first])   continue;
            visited[cur.first] = true;
            for(int i = 1; i <= n; i++){
                if(!visited[i] && grid[cur.first][i] != INT_MAX && grid[cur.first][i] + mindist[cur.first] < mindist[i]){
                    mindist[i] = grid[cur.first][i] + mindist[cur.first];
                    pq.push(make_pair(i, mindist[i]));
                }
            }
        }

        int res = 0;
        for(int i = 1; i <= n; i++){
            if(mindist[i] == INT_MAX)   return -1;
            res = max(res, mindist[i]);
        }
        return res;
    }
};

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值