Dijkstra学习笔记(模板+acwing上的题)

前言和模板

参考文档
第一次接触 D i j k s t r a Dijkstra Dijkstra是在大一下复习数据结构期末考试看了下思路。现在刚开始大二下,打卡群写kuangbin专题最短路不得不学下 D i j k s t r a Dijkstra Dijkstra🤣,事实证明(1)我很懒(2)人的潜力不容小觑,我曾以为自己永远也看不懂与图相关的算法。

再说下 D i j k s t r a Dijkstra Dijkstra的适用前提:

  1. 带权图的最短路径
  2. 权值都为非负值
  3. 单点出发到其它点

言归正传,直接上蒟蒻总结的模板吧。

(1)稠密图

直接用邻接数组的形式存

//从1到n的最短距离
int dij()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0; //含义是到自身的距离为0
    //大循环只是计次数
    for(int _ = 0; _ < n; ++ _)
    {
        int t = -1; //寻找当前的最短路
        for(int i = 1; i <= n; ++ i)
            if(!b[i] && (t == -1 || dist[t] > dist[i])) //短路原则
                t = i;
        
        //t是所有未到达的点中的最短路
        b[t] = true;
        //尝试更新距离数组
        for(int i = 1; i <= n; ++ i)
            if(dist[t] + a[t][i] < dist[i])
                dist[i] = dist[t] + a[t][i];
    }
    if(dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}

(2)稀疏图

稀疏图的节点数可高达 1 0 5 10^5 105,这个时候用朴素 D i j k s t r a Dijkstra Dijkstra算法一定超时。于是优化版的 D i j k s t r a Dijkstra Dijkstra算法诞生了。

为什么可以用堆优化?我们发现朴素版的“寻找当前的最短路”和“尝试更新距离”部分是从 1 1 1 n n n硬跑一遍,这一步骤是稀疏图超时的原因。而堆的完全二叉树特性将“找寻当前最短路“的时间复杂度压缩成 l o g ( n ) log(n) log(n)

使用邻接链表存储边与边的权值

//求1到n的最短距离
//一个点能被更新的前提是它的前驱被更新
int dij(int n)
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;
    priority_queue<PII, vector<PII>, greater<PII>> q; //需要小根堆依据距离排序
    q.push({0, 1});
    while(q.size())
    {
        auto tt = q.top(); q.pop();
        int t = tt.second;
        if(b[t]) continue;
        b[t] = true;
        //更新这个点的后继
        for(int i = 0; i < a[t].size(); ++ i)
        {
            int newnode = a[t][i].first, val = a[t][i].second;
            if(dist[newnode] > dist[t] + val)
            {
                dist[newnode] = dist[t] + val;
                q.push({dist[newnode], newnode}); //距离 节点
            }
        }
    }
    
    if(dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}

例题 点击题目跳转链接

例一、Dijkstra求最短路 I

板子题,题面不再赘述。

参考代码1 朴素Dijkstra
#include <bits/stdc++.h>

using namespace std;
const int N = 505;
int a[N][N];
int dist[N];
bool b[N];
int n, m; 

//从1到n的最短距离
int dij()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;
    for(int _ = 0; _ < n; ++ _)
    {
        int t = -1;
        for(int i = 1; i <= n; ++ i)
            if(!b[i] && (t == -1 || dist[t] > dist[i]))
                t = i;
        
        b[t] = true;
        for(int i = 1; i <= n; ++ i)
            if(dist[t] + a[t][i] < dist[i])
                dist[i] = dist[t] + a[t][i];
    }
    if(dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}

int main()
{
    ios::sync_with_stdio(false);
    memset(a, 0x3f, sizeof a);
    cin >> n >> m;
    
    int x, y, z;
    for(int i = 0; i < m; ++ i)
    {
        cin >> x >> y >> z;
        a[x][y] = min(a[x][y], z);
    }
    
    cout << dij();
    return 0;
}
参考代码2 堆版本(稠密图不需要堆优化,演示下堆怎么写)
//从1到n的最短距离
int dij()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;
    priority_queue<PII, vector<PII>, greater<PII>> q; //距离 下标
    q.push({0, 1});
    while(q.size())
    {
        auto tt = q.top(); q.pop();
        int t = tt.second;
        if(b[t]) continue;
        b[t] = true;
        
        for(int i = 1; i <= n; ++ i)
        {
            int newnode = i, val = a[t][i];
            if(dist[newnode] > dist[t] + val)
            {
                dist[newnode] = dist[t] + val;
                q.push({dist[newnode], newnode});
            }
        }
    }
    
    if(dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}

例二、Dijkstra求最短路 II

和例一不同的是结点数高达 1 0 5 10^5 105,此时用图的存储方式将由邻接矩阵变为邻接链表

堆优化版代码(不能用朴素法)
#include <bits/stdc++.h>

using namespace std;
const int N = 1e5 + 5e4 + 5;
typedef pair<int, int> PII;
int dist[N];
vector<vector<PII>> a;
bool b[N];

int dij(int n)
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;
    priority_queue<PII, vector<PII>, greater<PII>> q; //需要小根堆依据距离排序
    q.push({0, 1});
    while(q.size())
    {
        auto tt = q.top(); q.pop();
        int t = tt.second;
        if(b[t]) continue;
        b[t] = true;
        for(int i = 0; i < a[t].size(); ++ i)
        {
            int newnode = a[t][i].first, val = a[t][i].second;
            if(dist[newnode] > dist[t] + val)
            {
                dist[newnode] = dist[t] + val;
                q.push({dist[newnode], newnode});
            }
        }
    }
    
    if(dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}

int main()
{
    ios::sync_with_stdio(false);
    int n, m; cin >> n >> m;
    a.resize(n + 1);
    
    int x, y, z;
    for(int i = 0; i < m; ++ i)
    {
        cin >> x >> y >> z;
        a[x].push_back({y, z});
    }
    
    cout << dij(n);
    
    return 0;
}

例三、货物运输

写了两个裸题后再来一道非裸题结个尾吧

题目

给定一个 n 个点 m 条边的无重边无自环的无向图。点的编号为 1∼n

现在,要从点 1 到点 n运货。

已知每条边的最大承重,请你计算一次最多可以运多少货物。

输入格式

第一行包含整数 T,表示共有 T 组测试数据。

每组数据第一行包含两个整数 n,m

接下来 m 行,每行三个整数 a,b,c,表示点 a 和点 b 之间有一条无向边,最大承重为 c

输出格式

每组数据先输出一行 Scenario #x,其中 x 是组别编号(从 1 开始)。

然后一行,输出一个整数,表示一次性最多可运货物数量。

最后输出一个空行。

数据范围

1 ≤ T ≤ 10 , 1≤T≤10, 1T10,
1 ≤ n ≤ 1000 , 1≤n≤1000, 1n1000,

1 ≤ m ≤ n ( n − 1 ) 2 1≤m≤\frac{n(n−1)}{2} 1m2n(n1),
1 ≤ a , b ≤ n , 1≤a,b≤n, 1a,bn,
1 ≤ c ≤ 1 0 6 1≤c≤10^6 1c106
保证一定有解。

输入样例:

1
3 3
1 2 3
1 3 4
2 3 5

输出样例:

Scenario #1:
4
问题分析
  1. 权值都非负且问题和"最短路"相关,用 D i j k s t r a Dijkstra Dijkstra算法
  2. 求的值是所有路径中最小值集合的最大值
  3. d i s t dist dist数组存最小的承重量即可,越大越好
参考代码1 朴素Dijkstra
#include <bits/stdc++.h>

using namespace std;
typedef pair<int, int> PII;
const int N = 1005;
int a[N][N];
int dist[N];
bool b[N];

int dij(int n)
{
    dist[1] = 2e9;
    for(int _ = 0; _ < n; ++ _)
    {
        int t = -1;
        for(int i = 1; i <= n; ++ i)
            if(!b[i] && (t == -1 || dist[t] < dist[i]))
                t = i;
        b[t] = true;
        
        for(int i = 1; i <= n; ++ i)
            if(dist[i] < min(dist[t], a[t][i]))
                dist[i] = min(dist[t], a[t][i]);   
    }
    return dist[n];
}

int main()
{
    ios::sync_with_stdio(false);
    int T, n, m; cin >> T;
    
    for(int i = 1; i <= T; ++ i)
    {
        memset(a, 0, sizeof a);
        memset(dist, 0, sizeof dist);
        memset(b, 0, sizeof b);
        int n, m; cin >> n >> m;
        int x, y, z;
        for(int _ = 0; _ < m; ++ _)
        {
            cin >> x >> y >> z;
            a[x][y] = a[y][x] = z;
        }
        cout << "Scenario #" << i << ":" << endl;
        cout << dij(n) << endl << endl;
    }
    
    return 0; 
}
参考代码2 堆版本
int dij(int n)
{
    priority_queue<PII> q; //从1到n越大越好
    q.push({2e9, 1});
    while(q.size())
    {
        auto [v, t] = q.top(); q.pop();
        if(b[t]) continue;
        b[t] = true;
        for(int i = 1; i <= n; ++ i)
        {
            if(min(v, a[t][i]) > dist[i]) //当前承重和新的承重
            {
                dist[i] = min(v, a[t][i]);
                q.push({dist[i], i});
            }
        }
    }
    
    return dist[n];
}

总结

  1. 模板写在了开头
  2. 求最短路时要注意题意是最大值还是最小值;相加还是取最小(大)值
  3. 最小值用小根堆;最大值用大根堆
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用\[1\]:Dijkstra算法是一种用于解决单源最短路径问算法。它通过不断更新起始点到其他点的最短距离来找到最短路径。\[1\]引用\[2\]:在稀疏图中,节点数较多时,朴素的Dijkstra算法可能会超时。为了解决这个问,堆优化版的Dijkstra算法应运而生。\[2\]引用\[3\]:Dijkstra算法的实现通常使用邻接链表来存储边和权值。算法的核心思想是通过优先队列来选择当前距离起始点最近的节点,并更新其后继节点的最短距离。\[3\] 问: Dijkstra算法是用来解决什么问的?它的实现原理是什么? 回答: Dijkstra算法是用来解决单源最短路径问算法,即找到起始点到其他点的最短路径。它的实现原理是通过不断更新起始点到其他点的最短距离来找到最短路径。在稀疏图中,节点数较多时,可以使用堆优化版的Dijkstra算法来提高效率。该算法使用邻接链表来存储边和权值,并通过优先队列来选择当前距离起始点最近的节点,并更新其后继节点的最短距离。 #### 引用[.reference_title] - *1* *2* *3* [Dijkstra学习笔记模板+acwing上的)](https://blog.csdn.net/weixin_51368613/article/details/123114128)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值