最短路径例题(Floyd、Dijkstra)

关于这两个最短路径的算法的阐述和理解,可以看本人的这篇文章,希望能给你们带来帮助:
算法学习:最短路径
一、弗洛伊德最短路径算法
题目描述
在带权有向图G中,求G中的任意一对顶点间的最短路径问题,也是十分常见的一种问题。
解决这个问题的一个方法是执行n次迪杰斯特拉算法,这样就可以求出每一对顶点间的最短路径,执行的时间复杂度为O(n3)。
而另一种算法是由弗洛伊德提出的,时间复杂度同样是O(n3),但算法的形式简单很多。
在本题中,读入一个有向图的带权邻接矩阵(即数组表示),建立有向图并按照以上描述中的算法求出每一对顶点间的最短路径长度。

输入
输入的第一行包含1个正整数n,表示图中共有n个顶点。其中n不超过50。
以后的n行中每行有n个用空格隔开的整数。对于第i行的第j个整数,如果大于0,则表示第i个顶点有指向第j个顶点的有向边,且权值为对应的整数值;如果这个整数为0,则表示没有i指向j的有向边。当i和j相等的时候,保证对应的整数为0。
输出
共有n行,每行有n个整数,表示源点至每一个顶点的最短路径长度。如果不存在从源点至相应顶点的路径,输出-1。对于某个顶点到其本身的最短路径长度,输出0。
请在每个整数后输出一个空格,并请注意行尾输出换行。
样例输入
4
0 3 0 1
0 0 4 0
2 0 0 0
0 0 1 0
样例输出
0 3 2 1
6 0 4 7
2 5 0 3
3 6 1 0

分析:就是弗洛伊德算法的板子题,但是题目要求的是,相同的点输入一定为0,还有没有连通的时候距离也输入0,我们只需要在主函数做处理,换成INF就好了。

代码如下:

#include<bits/stdc++.h>
using namespace std;
const int INF=1e6;
const int NUM=55;
int n;
int graph[NUM][NUM];
void floyd(){
    for(int k=1;k<=n;++k)
        for(int i=1;i<=n;++i)
            if(graph[i][k]!=INF)
                for(int j=1;j<=n;++j)
                    if(graph[i][j]>graph[i][k]+graph[k][j])
                        graph[i][j]=graph[i][k]+graph[k][j];
    for(int s=1;s<=n;++s){
        for(int i=1;i<=n;++i)
            if(s==i)printf("0 ");
            else if(graph[s][i]!=INF)printf("%d ",graph[s][i]);
            else printf("-1 ");
        printf("\n");
    }
}
int main(){
    int len;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;++j){
            scanf("%d",&len);
            if(len)graph[i][j]=len;
            else graph[i][j]=INF;
        }
    floyd();
}

2、迪杰斯特拉最短路径算法
题目描述
在带权有向图G中,给定一个源点v,求从v到G中的其余各顶点的最短路径问题,叫做单源点的最短路径问题。
在常用的单源点最短路径算法中,迪杰斯特拉算法是最为常用的一种,是一种按照路径长度递增的次序产生最短路径的算法。
在本题中,读入一个有向图的带权邻接矩阵(即数组表示),建立有向图并按照以上描述中的算法求出源点至每一个其它顶点的最短路径长度。
输入
输入的第一行包含2个正整数n和s,表示图中共有n个顶点,且源点为s。其中n不超过50,s小于n。
以后的n行中每行有n个用空格隔开的整数。对于第i行的第j个整数,如果大于0,则表示第i个顶点有指向第j个顶点的有向边,且权值为对应的整数值;如果这个整数为0,则表示没有i指向j的有向边。当i和j相等的时候,保证对应的整数为0。
输出
只有一行,共有n-1个整数,表示源点至其它每一个顶点的最短路径长度。如果不存在从源点至相应顶点的路径,输出-1。
请注意行尾输出换行。
样例输入
4 1
0 3 0 1
0 0 4 0
2 0 0 0
0 0 1 0
样例输出
6 4 7

分析:处理0和上述是一样的步骤,不过按这个样例来看,可以比较容易看出结点是从0开始算的,所以样例输入的s为1,实际上就是结点2了。注意这点就好了。

代码如下:

#include<bits/stdc++.h>
using namespace std;
const int INF=1e6;
const int NUM=55;
int n;
struct edge{
    int from,to,w;
    edge(int a,int b,int c){from=a;to=b;w=c;}
};
vector<edge>e[NUM];
struct s_node{
    int id,n_dis;
    s_node(int b,int c){id=b;n_dis=c;}
    bool operator<(const s_node &a)const
    {return n_dis>a.n_dis;}
};
void dijkstra(int s){
    int dis[NUM];
    bool done[NUM];
    for(int i=1;i<=n;i++)dis[i]=INF,done[i]=false;
    dis[s]=0;
    priority_queue<s_node>Q;
    Q.push(s_node(s,dis[s]));
    while(!Q.empty()){
        s_node u=Q.top();
        Q.pop();
        if(done[u.id])true;
        for(int i=0;i<e[u.id].size();++i){
            edge y=e[u.id][i];
            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]));
            }
        }
    }
    for(int i=1;i<=n;i++)
        if(i!=s){
            if(dis[i]!=INF)printf("%d ",dis[i]);
            else printf("-1 ");
        }
    printf("\n");
}
int main(){
    int len,s;
    scanf("%d%d",&n,&s);
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j){
            scanf("%d",&len);
            if(len)e[i].push_back(edge(i,j,len));
            else e[i].push_back(edge(i,j,INF));
        }
    dijkstra(s+1);
}

3、单源最短路径问题
题目描述
编程实现Dijkstra算法,求一个有向加权图中,从源点出发到其他各个顶点的最短路径。
输入
第1行第1个值表示顶点个数,第2个值表示边个数;第2行开始为边(两个顶点,边的起点和终点)及权重。
输出
顶点0到每一个顶点的最短路径长度。
样例输入
5 7
0 1 10
0 3 30
0 4 100
1 2 50
2 4 10
3 2 20
3 4 60
样例输出
0 10 50 30 60

代码如下:

#include<bits/stdc++.h>
using namespace std;
const int INF=1e6;
const int NUM=55;
int n;
struct edge{
    int from,to,w;
    edge(int a,int b,int c){from=a;to=b;w=c;}
};
vector<edge>e[NUM];
struct s_node{
    int id,n_dis;
    s_node(int b,int c){id=b;n_dis=c;}
    bool operator<(const s_node &a)const
    {return n_dis>a.n_dis;}
};
void dijkstra(int s){
    int dis[NUM];
    bool done[NUM];
    for(int i=1;i<=n;i++)dis[i]=INF,done[i]=false;
    dis[s]=0;
    priority_queue<s_node>Q;
    Q.push(s_node(s,dis[s]));
    while(!Q.empty()){
        s_node u=Q.top();
        Q.pop();
        if(done[u.id])true;
        for(int i=0;i<e[u.id].size();++i){
            edge y=e[u.id][i];
            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]));
            }
        }
    }
    for(int i=0;i<n;i++)
        printf("%d%c",dis[i],i==n-1?'\n':' ');
}
int main(){
    int len,E,x,y;
    scanf("%d%d",&n,&E);
    while(E--){
        scanf("%d%d%d",&x,&y,&len);
        e[x].push_back(edge(x,y,len));
    }
    dijkstra(0);
}

<think>好的,我现在需要帮用户了解蓝桥杯竞赛Dijkstra最短路径(包括堆优化)和Floyd多源最短路径的相关内容及例题。首先,我应该先整理这两个算法的基本概念、实现步骤以及优化方法,然后结合具体的例题进行分析。 首先,Dijkstra算法是单源最短路径算法,适用于没有负权边的图。用户提到了堆优化,这应该是针对传统Dijkstra的时间复杂度进行优化的方法。传统Dijkstra使用邻接矩阵或邻接表,时间复杂度为$O(n^2)$,而堆优化可以将复杂度降到$O(m \log n)$,其中m是边数,n是节点数。需要解释堆优化的原理,比如使用优先队列(堆)来每次快速取出当前距离最小的节点。 接下来是Floyd算法,用于多源最短路径,基于动态规划的思想。它的时间复杂度是$O(n^3)$,适合节点数较少的场景。要说明Floyd的三重循环结构,以及如何通过中间节点更新最短路径。 然后,需要结合蓝桥杯的例题。比如用户提到的2022省赛题目“路径”可能涉及到Dijkstra的应用,而2021国赛的“交通规则”可能与Floyd或动态规划相关。需要找到具体的题目要求,分析解题思路,并给出代码模板或关键步骤。 同时,用户提供的引用中有关于Bellman-Ford、SPFA和动态规划的内容,可能需要将这些与DijkstraFloyd进行对比,说明各自的适用场景和优缺点。例如,Dijkstra不能处理负权边,而Bellman-Ford可以,但效率较低;SPFA是Bellman-Ford的队列优化版本。Floyd适用于多源问题,但节点数不宜过多。 在回答结构上,需要分点清晰,先介绍算法思想,再讲实现步骤,接着是优化方法,然后结合例题分析,最后总结应用场景。代码部分需要用正确的LaTeX格式,行内公式用$...$,独立公式用$$...$$,并且确保代码示例正确无误。例如,Dijkstra的堆优化可以使用优先队列实现,Python中可以用heapq模块。 另外,用户提到蓝桥杯备赛指南的参考链接,可能需要引用相关内容,比如在动态规划或状态压缩部分,但用户的问题主要集中在最短路径算法,所以重点应放在DijkstraFloyd上。不过,如果例题中涉及动态规划思想,比如交通规则问题,可能需要结合动态规划进行分析。 需要验证用户提供的引用内容是否与问题相关。引用[1]提到Bellman-ford和SPFA,这可能与Dijkstra形成对比,可以简要提及。引用[4]讨论了为什么选择dist[v]最小就能确定最短路径,这涉及Dijkstra的贪心策略的正确性证明,可以用反证法说明,这可能在解释算法时用到。 最后,生成相关问题,确保问题与内容相关,帮助用户进一步复习。例如,DijkstraFloyd的区别,堆优化的实现方式,Floyd的动态规划思想等。 总结步骤: 1. 分别介绍Dijkstra算法及其堆优化,给出代码模板。 2. 介绍Floyd算法的动态规划思想,给出代码模板。 3. 分析蓝桥杯例题,如2022省赛“路径”和2021国赛“交通规则”。 4. 对比算法优缺点和应用场景。 5. 生成相关问题。 需要确保所有数学公式正确使用LaTeX格式,代码示例正确,引用适当。</think>### 蓝桥杯竞赛DijkstraFloyd算法详解 #### 一、Dijkstra最短路径算法 **算法思想** Dijkstra算法用于解决**单源最短路径**问题(无负权边),通过贪心策略逐步确定从起点到各顶点的最短距离。其核心思想是:每次选择当前未标记的**距离起点最近**的顶点$u$,并用$u$的边松弛相邻顶点$v$的距离。若$dist[v] > dist[u] + w(u,v)$,则更新$dist[v]$[^4]。 **堆优化实现** 传统Dijkstra时间复杂度为$O(n^2)$,堆优化(优先队列)可降低到$O(m \log n)$。以下是Python代码模板: ```python import heapq def dijkstra_heap(graph, start): dist = {node: float('inf') for node in graph} dist[start] = 0 heap = [(0, start)] while heap: current_dist, u = heapq.heappop(heap) if current_dist > dist[u]: continue for v, w in graph[u].items(): if dist[v] > dist[u] + w: dist[v] = dist[u] + w heapq.heappush(heap, (dist[v], v)) return dist ``` **例题分析(2022省赛“路径”)** 题目要求计算图中节点1到节点2022的最短路径,边权为两数的最小公倍数。解题思路: 1. 构建图的邻接表,边权为$lcm(u, v)$; 2. 使用堆优化Dijkstra计算最短路径。 --- #### 二、Floyd多源最短路径算法 **动态规划思想** Floyd算法用于**多源最短路径**,时间复杂度$O(n^3)$,适用节点数较少的情况。定义状态转移方程: $$ dist[i][j] = \min(dist[i][j], dist[i][k] + dist[k][j]) $$ 通过三重循环枚举中间节点$k$,更新所有节点对$(i,j)$的最短距离。 **代码模板** ```python def floyd(n, edges): dist = [[float('inf')] * (n+1) for _ in range(n+1)] for i in range(n+1): dist[i][i] = 0 for u, v, w in edges: dist[u][v] = w for k in range(1, n+1): for i in range(1, n+1): for j in range(1, n+1): dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]) return dist ``` **例题分析(2021国赛“交通规则”)** 题目涉及动态调整交通规则后的最短路径变化。需结合Floyd的动态规划特性,预处理所有可能的中转节点对最短路径的影响。 --- #### 三、算法对比与应用场景 | 算法 | 类型 | 时间复杂度 | 适用场景 | |---------------|------------|--------------|--------------------------| | Dijkstra+堆优化 | 单源 | $O(m \log n)$| 无负权边,稀疏图 | | Floyd | 多源 | $O(n^3)$ | 节点数少,需多源最短路径 | ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

布布要成为最强的人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值