最短路算法——Dijkstra(题目练习解析)

本文详细介绍了迪杰斯特拉算法的原理及应用,通过一个王国问题的实例展示了算法的运用。利用邻接表和优先队列实现,解决从皇宫到各个建筑的最短路径问题。同时,提供了C++代码实现,并解释了如何打印最短路径。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Dijkstra(题目练习解析)



点击 -> Dijkstra算法详细讲解


王国

题目描述

小明是王国的王子,今天是他登基之日。在即将成为国王之前,老国王给他出了道题,他想要考验小明是否有能力管理国家。

题目的内容如下:

蓝桥王国一共有 N 个建筑和 M 条单向道路,每条道路都连接着两个建筑,每个建筑都有自己编号,分别为 1∼N 。(其中皇宫的编号为 1)

国王想让小明回答从皇宫到每个建筑的最短路径是多少,但紧张的小明此时已经无法思考,请你编写程序帮助小明回答国王的考核。

输入描述

输入第一行包含三个正整数 N,M。

第 22 到 M + 1 行每行包含三个正整数 u,v,w,表示 uv 之间存在一条距离为 w 的路。

1 ≤ ≤ 3×10^5,1 ≤ ≤ 10^6,1 ≤ ui​,vi​ ≤ N,0 ≤ wi​ ≤ 10^9。

输出描述

输出仅一行,共 N 个数,分别表示从皇宫到编号为 1∼N 建筑的最短距离,两两之间用空格隔开。(如果无法到达则输出 -1)

样例输入

3 3 
1 2 1
1 3 5
2 3 2

样例输出

0 1 3

 参考代码:

 “邻接表 + 优先队列”

  1. 用邻接表存图和查找邻居。对邻居的查找和扩展,是通过动态数组 vector <edge> e[NUM] 实现的邻接表,和 SPFA 一样。其中 e[i] 存储第 i 个结点上所有的边,边的一头是它的邻居,即 struct edge 的参数 to。需要扩展结点 i 的邻居的时候,查找 e[i] 即可。已经放到集合 A 中的结点,不要扩展;程序中用 bool done[NUM] 记录集合 A,当 done[i] = true 时,表示它在集合 A 中,已经找到了最短路。
  2. 在集合 B 中找距离起点最短的结点。直接用 STL 的优先队列实现,程序中是 priority_queue <s_node> Q。但是有关丢弃的动作,STL的优先队列无法做到。例如在 “Dijkstra执行过程”的步骤 3 中,需要在 B={(2-5), (2-4), (4,7)} 中丢弃 (2- 5),但是 STL 没有这种操作。在程序中也是用 bool done[NUM] 协助解决这个问题。从优先队列 pop 出 (2-4)时,记录 done[2] = true,表示结点 2 已经处理好。下次从优先队列 pop 出 (2-5)时,判断 done[2] 是 true,丢弃。
  3. Dijkstra 打印最短路径非常容易。定义 pre[] 记录前驱结点,然后用 print_path() 打印整个路径。
#include<bits/stdc++.h>
using namespace std;

const long long INF = 0x3f3f3f3f3f3f3f3fLL; //这样定义INF的好处是: INF <= INF+x
const int NUM = 3e5+2;

struct edge{
    int from, to; long long w;
//边:起点,终点,权值。起点from并没有用到,e[i]的i就是from
    edge(int a, int b,long long c){from=a; to=b; w=c;}
};
vector<edge>e[NUM];           //用于存储图
struct s_node{
    int id; long long n_dis;   //id:结点;n_dis:这个结点到起点的距离
    s_node(int b,long long c){id=b; n_dis=c;}
    bool operator < (const s_node & a) const
    { return n_dis > a.n_dis;}
};
int n,m;
int pre[NUM]; //记录前驱结点
void print_path(int s, int t) {       //打印从s到t的最短路
    if(s==t){ printf("%d ", s); return; }  //打印起点
    print_path(s, pre[t]);              //先打印前一个点
    printf("%d ", t);                    //后打印当前点。最后打印的是终点t
}
long long  dis[NUM];         //记录所有结点到起点的距离
void dijkstra(){
    int s = 1;             //起点s是1
    bool done[NUM]; //done[i]=true表示到结点i的最短路径已经找到
    for (int i=1;i<=n;i++) {dis[i]=INF; done[i]=false; }    //初始化
    dis[s]=0;                           //起点到自己的距离是0
    priority_queue <s_node> Q;       //优先队列,存结点信息
    Q.push(s_node(s, dis[s]));       //起点进队列
    while (!Q.empty())   {
        s_node u = Q.top();           //pop出距起点s距离最小的结点u
        Q.pop();
        if(done[u.id])  //丢弃已经找到最短路径的结点。即集合A中的结点
            continue;
        done[u.id]= true;
        for (int i=0; i<e[u.id].size(); i++) {  //检查结点u的所有邻居
            edge y = e[u.id][i];         //u.id的第i个邻居是y.to
            if(done[y.to])               //丢弃已经找到最短路径的邻居结点
                continue;
            if (dis[y.to] > y.w + u.n_dis) {
                dis[y.to] = y.w + u.n_dis;
                Q.push(s_node(y.to, dis[y.to]));
                        //扩展新的邻居,放到优先队列中
                pre[y.to]=u.id;  //如果有需要,记录路径
            }
        }
    }
    // print_path(s,n);         //如果有需要,打印路径: 起点1,终点n
}


int main(){
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
        e[i].clear();
    while (m--) {
        int u,v,w;
        scanf("%d%d%lld",&u,&v,&w);
        e[u].push_back(edge(u,v,w));
     // e[v].push_back(edge(v,u,w));    //本题是单向道路
    }
    dijkstra();
    for(int i=1;i<=n;i++){
        if(dis[i]>=INF) cout<<"-1 ";
        else printf("%lld ", dis[i]);
    }

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值