最短路问题

最短路问题是图论里非常经典的一个考点

接下来着重讲述五种求最短路的算法:朴素版dijkstra算法、堆优化版的dijkstra算法、bellman-ford算法、spfa算法、floyd算法

总体思维导图:

总体思路:

最短路分为两大类
{
在以下给出的时间复杂度中n表示图中的点数,m表示图中的边数

源点就是起点,汇点就是终点
    ①单源最短路{
    求一个点到其它所有点的最短距离,(比如:从1号点到n的最短路问题)。
    ①{
        1、所有边权都是正的
        {
            一、朴素的Dijkstra算法(O(n^2))
            二、堆优化的Dijkstra算法(O(mlogn))
            
            如果图上的边数越稠密(当m和n^2差不多的时候即为稠密图),那么堆优化版的要比朴素版的时间复杂度要高。反之如果边是稀疏的,那么尽量使用堆优化般的。
          
            稠密图用邻接矩阵(矩阵),稀疏图用邻接表(链表)

朴素的Dijkstra板子:
void dijkstra()
{memset(dist, 0x3f, sizeof dist);dist[1] = 0;for (int i = 0; i < n - 1; i ++ ){int t = -1;     // 在还未确定最短路的点中,寻找距离最小的点for (int j = 1; j <= n; j ++ )if (!st[j] && (t == -1 || dist[t] > dist[j]))t = j;// 用t更新其他点的距离for (int j = 1; j <= n; j ++ )dist[j] = min(dist[j], dist[t] + g[t][j]);st[t] = true;}}

堆优化的Dijkstra板子:
int dijkstra()
{dist[1] = 0;priority_queue<PII, vector<PII>, greater<PII>> heap;heap.push({0, 1});      // first存储距离,second存储节点编号while (heap.size()){auto t = heap.top();heap.pop();int ver = t.second, distance = t.first;if (st[ver]) continue;st[ver] = true;for (int i = h[ver]; i != -1; i = ne[i]){int j = e[i];if (dist[j] > distance + w[i]){dist[j] = distance + w[i];heap.push({dist[j], j});}}}}


        }
        2、存在负权边
        {
            一、Bellman-Ford算法 O(nm)
            二、SPFA   一般情况下O(m),最坏O(nm)。SPFA算法也就是对Bellman-Ford算法的优化,
            若对边数进行了限制就不能使用SPFA算法,只能使用Bellman-ford算法。

一般情况下,题目中不会对边数进行限制,所以99%的情况SPFA算法要比Bellman-ford算法好用的多。

Bellman-Ford板子:备份
struct Edge
{//a,b,w从a走向b的边,权重是wint a,b,w;
}edges[M];int ballman_ford()
{memset(dist,0x3f,sizeof dist);dist[1]=0;//最多经历k次for(int i=0;i<k;i++){//先复制一层,防止串联memcpy(backup,dist,sizeof dist);for(int j=0;j<m;j++){//调用结构体auto e=edges[j];//等于上一个点+权重的值 与 当前这个点到原点的距离最小值dist[e.b]=min(backup[e.a]+e.w,dist[e.b]);}}
SPFA板子:每个被更新的节点都用队列存储进来,注意使用st[ ]来优化,防止重复进入
void spfa()
{dist[1] = 0;queue<int> q;q.push(1);st[1] = true;while (q.size()){auto t = q.front();q.pop();st[t] = false;for (int i = h[t]; i != -1; i = ne[i]){int j = e[i];if (dist[j] > dist[t] + w[i]){dist[j] = dist[t] + w[i];if (!st[j])     // 如果队列中已存在j,则不需要将j重复插入{q.push(j);st[j] = true;}}}}
}


        }
     }
    }
    ②多源汇最短路{
        不会像单源最短路那样只有一个起点。多源汇最短路可能有多个起点,对于多次询问,从其中一个点走到另外一个点的最短路问题,起点和终点都是不确定的
        
        Floyd算法 O(N^3)

Floyd板子:三重循环
void floyd()
{for (int k = 1; k <= n; k ++ )for (int i = 1; i <= n; i ++ )for (int j = 1; j <= n; j ++ )d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}


    }
}

朴素版的dijkstra:  O(MN)

题目:849. Dijkstra求最短路 I - AcWing题库

 本题思路:

到起点的距离:dist[i]。已经确定了最短距离的点的集合:s[i]

第一步 先初始化距离:dist[1]=0,其余的dist[i]= +∞(比较大的数)
因为一开始只有起点的距离是被确定的了,其余的所有点都是不确定的

1号点到n号点最近的距离最近也就是n号点到1号点的距离最近,而由于这里1号点的到起点的距离已经确定所以一开始更新迭代就利用dist[1]已知的条件,来更新其他点到1号点的距离dist[x],然后再更新其他点到点x的距离即可。
这期间如果1号点到n号点的距离已经求出dist[n],且dist[x]<dist[n],那么dist[x]的距离就是确定了的(继续以这点迭代下去)。
如果不小于dist[n],那就没必要继续迭代下去,因为按照x的这条路径的长度会增加,那么就更一定不小于dist[n],所以更新到n号点时,那么n号点到1号点的距离也就最近了

第二步 是一个迭代循环的过程:
for(int i=0,i<n;i++)
{
    t=不在s中的所有点中距离起点最近的点(距离最近的点)
    s⬅t (如果有的话就把t加到s里面去)
    更新dist[i](用t更新,即如果dist[i]>dist[t]+w(w是权重)就用t更新)
}


模拟:

在图论中寻找最短路的大体思维就是这样的,后面的算法也是如此,不在给出

 

 

 

 

代码 
#include<iostream>
#include<cstring>
#include<algorithm>using namespace std;const int N=510;int n,m;
int g[N][N];
//到起点的距离
int dist[N];
bool st[N];int dijkstra()
{memset(dist,0x3f,sizeof dist);dist[1]=0;//找到当前没有确定的点当中,距离起点最近的那个点(然后以这个点更新迭代之后的点)//如果已经确定了,那么就和dist[n]进行比较,来确定下一步是继续迭代还是结束这条路径for(int i=0;i<n;i++){int t=-1;for(int j=1;j<=n;j++){if(!st[j] && (t==-1 || dist[t] >dist[j]))//确定dist[j];t=j;}//已经确定了dist[t];st[t]=true;//更新每个点到1号点的距离for(int j=1;j<=n;j++){//使其等于(j号点到1号点的距离)与(t号点到1号点的距离+t号点到j号点距离的最小值)dist[j]=min(dist[j],dist[t]+g[t][j]);}}if(dist[n]==0x3f3f3f3f) return -1;else return dist[n];
}int main()
{cin >> n >> m;//初始化所有点之间的距离都为一个较大的数memset(g,0x3f,sizeof g);while(m--){int a,b,c;cin >> a >> b >> c;//防止重边g[a][b]=min(g[a][b],c);}cout << dijkstra();return 0;
}

 

 堆优化版的dijkstra: O(mlogn)

题目:850. Dijkstra求最短路 II - AcWing题库

 这里是稀疏图,使用邻接表来存

 代码:

/*
n表示图中的点数,m表示图中的边数
一、朴素的Dijkstra算法(O(n^2))
二、堆优化的Dijkstra算法(O(mlogn))使用朴素版的dijkstra必定会爆掉,所以采用堆优化
由于点数和边数大致差不多,所以在这里是稀疏图→使用邻接表来存
这里采用优先队列。
*/#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>using namespace std;typedef pair<int,int> PII;
const int N=150010;
int n,m;
int h[N],w[N],e[N],ne[N],idx;
int dist[N];
bool st[N];void add(int a,int b,int c)
{e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}int dijkstra()
{dist[1]=0;//这里是小根堆,因为是从一号点开始priority_queue<PII,vector<PII>,greater<PII>> heap;
//这里先放距离,后放点的原因是pair<int,int>类的排序,是先比较的first,如果first相同才比较second//一号点,距离为0先放入队列中heap.push({0,1});while(heap.size()){auto t=heap.top();heap.pop();int ver=t.second,distance=t.first;//如果这个点已经使用过了的话,直接跳过if(st[ver])  continue;st[ver]=true;//使用这个点,以这个点为起点向下遍历//向下遍历,走到ver能走到的所有点for(int i=h[ver];i!=-1;i=ne[i]){//找出点坐标int tt=e[i];/*假设上一个点为x,下一个点为y。如果下一个点到原点的距离(dist[tt])大于(上一个点到原点的距离distance+x到y的距离(w[i]))时,才有更新的价值。*/if(dist[tt]>distance+w[i]){//符合,更新当前点到原点的距离dist[tt]=distance+w[i];//放入队列heap.push({dist[tt],tt});}}}if(dist[n]==0x3f3f3f3f) return -1;return dist[n];
}int main()
{scanf("%d%d",&n,&m);memset(dist,0x3f,sizeof dist);memset(h,-1,sizeof h);while(m--){int a,b,c;scanf("%d%d%d",&a,&b,&c);、//使用链表来存储add(a,b,c);}printf("%d\n",dijkstra());return 0;
}

Bellman-ford算法

题目:853. 有边数限制的最短路 - AcWing题库

代码:
#include<iostream>
#include<algorithm>
#include<cstring>using namespace std;//点数,边数
const int N=510,M=10010;
int n,m,k;int dist[N],backup[N];struct Edge
{//a,b,w从a走向b的边,权重是wint a,b,w;
}edges[M];int ballman_ford()
{memset(dist,0x3f,sizeof dist);dist[1]=0;//最多经历k次for(int i=0;i<k;i++){
//复制一份,防止串联memcpy(backup,dist,sizeof dist);for(int j=0;j<m;j++){auto e=edges[j];dist[e.b]=min(backup[e.a]+e.w,dist[e.b]);}}if(dist[n] > 0x3f3f3f3f /2) cout << "impossible" << endl;else cout << dist[n];
}int main()
{cin >> n >> m >> k;for(int i=0;i<m;i++){int a,b,c;cin >> a >> b >> c;edges[i]={a,b,c};}ballman_ford();return 0;
}

 

SPFA算法:

题目:851. spfa求最短路 - AcWing题库

 模拟:

 

代码:
/*
每次将变小了的节点入队,那么用此节点更新后面的节点时才有可能将后面的数值变小
否则,若此节点都没变小,那么后面的节点不一定会因为被此节点更新而变小
即优化的是Bellman-ford算法里的这一步:for (int i = 0; i < n; i ++ )for (int j = 0; j < m; j ++ )
这里并不能保证每一次的j循环都有节点被更新,从而增加时间复杂度(公司的总效益涨了,员工的工资才有可能会涨,即前面的点的dist数值减小了,后面的数值才可能会减小)
省去了因为没有被更新而重复的操作
但是如果出题人卡你,时间复杂度和bellman-ford算法一致O(nm),最好的情况为O(m);
*/#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>using namespace std;int n,m;
const int N=1e5+10;
int e[N],ne[N],h[N],idx,w[N];
queue<int> q;
bool st[N];
int dist[N];
//使用链表进行储存
void add(int a,int b,int c)
{e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}void spfa()
{memset(dist,0x3f,sizeof dist);dist[1]=0;q.push(1);//使用过1号节点,则为truest[1]=true;while(q.size()){int t=q.front();q.pop();/*取出来头节点之后,那么在队列中的头节点会被删除,也就需要清空其使用情况以方便下次的入队(只要这个节点变小了,那么就可以用它来再次更新后面的节点)也就是再次入队*/st[t]=false;//从头结点开始遍历,使用队列中的节点依次更新后面的值的大小for(int i=h[t];i!=-1;i=ne[i]){int j=e[i];if(dist[j]>dist[t]+w[i]){dist[j]=dist[t]+w[i];if(!st[j]){q.push(j);st[j]=true;}}}}//由于每次更新都是由队列里的数来更新,所以如果抵达不到n的话,那么n号点一定没有被更新if(dist[n]==0x3f3f3f3f) cout << "impossible" << endl;else cout << dist[n];
}int main()
{memset(h,-1,sizeof h);cin >> n >>m;while(m--){int a,b,c;cin >> a >> b >> c;add(a,b,c);}spfa();return 0;
}

 题目:852. spfa判断负环 - AcWing题库

这里再寻找最短路时,如若存在负环,那么会一直绕着这个负环来循环(因为这样可以使距离无限变小)

代码:
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>using namespace std;const int N=10010;
int n,m;
int e[N],ne[N],h[N],w[N],idx;
//cnt[]表示当前最短路的边数
int dist[N],cnt[N];
bool st[N];
queue<int> q;void add(int a,int b,int c)
{e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}bool spfa()
{//负环所在的位置可能是头节点抵达不到的,所以一开始将所有点全部放进队列中for(int i=1;i<=n;i++){//标记为true,表示该节点已经被放进了队列里st[i]=true;q.push(i);}while(q.size()){int t=q.front();q.pop();st[t]=false;for(int i=h[t];i!=-1;i=ne[i]){int j=e[i];if(dist[j]>dist[t]+w[i]){dist[j]=dist[t]+w[i];cnt[j]=cnt[t]+1;//如果不存在负环的话,那么从一个节点出发后所遍历的边数一定小于n//否则的话就一定存在负环if(cnt[j]>=n) return true;if(!st[i]){q.push(j);st[j]=true;}}}}return false;
}int main()
{memset(h,-1,sizeof h);cin >> n >> m;while(m--){int a,b,c;cin >> a >> b >> c;add(a,b,c);}if(spfa()) puts("Yes");else puts("No");return 0;
}

Floyed算法:

这里的证明使用dp来论证的,建议直接背板子

题目:854. Floyd求最短路 - AcWing题库

代码: 

/*
②多源汇最短路{不会像单源最短路那样只有一个起点。多源汇最短路可能有多个起点,对于多次询问,从其中一个点走到另外一个点的最短路问题,起点和终点都是不确定的Floyd算法 O(N^3)}用邻接矩阵存储所有的边d[][]for(k=1;k<=n;k++){for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){d[i][j]=min(d[i][j],d[i][k]+d[k][j])}}}循环完之后d[i][j]就是从i到j的最短路径
*/#include<iostream>
#include<algorithm>
#include<cstring>using namespace std;const int N=210,INF=1e9;
int d[N][N];
int n,m,Q;void floyd()
{for(int k=1;k<=n;k++){for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){d[i][j]=min(d[i][j],d[i][k]+d[k][j]);}}}
}int main()
{cin >> n >> m >> Q;//初始化for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){//自环的距离初始化为0if(i==j) d[i][j]=0;//其余的初始化为正无穷else d[i][j]=INF;}}while(m--){int a,b,c;cin >> a >> b >> c;//对于重边来说取一个最小值即可d[a][b]=min(d[a][b],c);}floyd();while(Q--){int a,b;cin >> a >> b;//存在负权边时距离的大小不等于INFif(d[a][b]>INF/2) cout << "impossible" << endl;else cout << d[a][b] << endl;}return 0;
}

 tips:

图论这一节重在代码实现,很多板子都是互相重复的。

至于该思路如何实现的,可以课下钻研

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/23112.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

学习笔记——路由网络基础——静态路由(static)

三、静态路由(static) 1、静态路由 (1)定义 静态路由(Static)&#xff1a;由管理员手动配置和维护的路由。静态路由配置简单&#xff0c;被广泛应用于网络中。此外还可以实现负载均衡和路由备份。 静态路由默认优先级为60&#xff0c;如果想在多条静态路由中让某条路由优选…

uniapp小程序开发 | 从零实现一款影视类app (后台接口实现,go-zero微服务的使用)

uniapp小程序开发实战系列&#xff0c;完整介绍从零实现一款影视类小程序。包含小程序前端和后台接口的全部完整实现。系列连载中&#xff0c;喜欢的可以点击收藏。 该篇着重介绍获取轮播图后台接口和获取正在热映电影的两个后台接口的实现。 后台服务使用golang&#xff0c;…

MySQL—多表查询—自连接

一、引言 自连接&#xff0c;顾名思义就是自己连接自己。 自连接的语法结构&#xff1a; 表 A 别名 A join 表 A 别名 B ON 条件 ...; 注意&#xff1a; 1、这种语法有一个关键字&#xff1a;join 2、自连接查询可以是内连接的语法&#xff0c;可以是外连接的语法&#xff08…

【游戏】Goc赚钱模拟器1.0版

Hello!大家好&#xff0c;我是学霸小羊&#xff0c;今天分享一个Goc游戏。 //注&#xff1a;以下代码为Goc原创代码。 大家可以在下面网址写入代码www.51goc.com慧通教育http://www.51goc.com注&#xff1a;Goc编辑器路径&#xff1a; www.51goc.com ➡ 登录 ➡ 游客登陆 ➡…

Three.js加入到可视化大屏,看看能否惊艳到你?

three.js 在可视化大屏上可以实现各种三维场景和动画效果&#xff0c;可以根据具体需求进行定制化开发&#xff0c;并结合其他技术&#xff0c;如数据可视化、交互设计等&#xff0c;实现更加丰富的可视化效果。 three.js 是一个基于 WebGL 的 JavaScript 3D 库&#xff0c;可…

循迹模块之循迹小车

1.TCRT5000传感器 TCRT5000传感器的红外发射二极管 不断发射红外线 1.1 当发射出的红外线没有被反射回来或被反射回来但强度不够大时&#xff0c; 红外接收管一直处于关断状态&#xff0c;此时模块的输出端为高电平&#xff0c;指示二极管一直处于熄灭状态 1.2 当被检测物体…

跳跃游戏二

方法一&#xff1a;&#xff08;双指针法&#xff09;此题参考跳台阶问题&#xff0c;题目要求求到达最后一个点的最小跳跃次数&#xff0c;那么我们就可以从最后一个往前推&#xff0c;先看谁能离得最远&#xff0c;并且能跳到最后一个。假设i位置是离最后一个位置最远&#x…

【新书上市】图像画质算法与底层视觉技术

图书主页&#xff1a;https://book.douban.com/subject/36895899/ 购买链接&#xff1a;https://item.jd.com/10105601481762.html 内容介绍 本书主要介绍了图像画质相关的各类底层视觉任务及其相关算法&#xff0c;重点讲解了去噪、超分辨率、去雾、高动态范围、图像合成与图…

Python语法详解module3(组合数据类型列表、元组、字典、集合详细用法)

目录 一、列表列表的创建多维列表列表的访问和修改列表的添加和删除列表的遍历使用 for 循环遍历使用 while 循环遍历同时遍历索引和元素列表推导式 常用的列表函数len()sort()reverse()index()count()extend()clear() 二、元组创建元组访问元组元素元组的不可变性元组的优点元…

Scalable Diffusion Models with Transformers

Metahttps://github.com/facebookresearch/DiT/tree/main?tabreadme-ov-file 问题引入 transformer架构的latent diffusion model&#xff0c;有较好的延展性并是sota&#xff1b; methods patchify&#xff1a;原图片 I ∈ R H W 3 I\in\mathbb{R}^{H\times W\times 3…

【微信小程序】模板语法

数据绑定 对应页面的 js 文件中 定义数据到 data 中&#xff1a; 在页面中使用 {{}} 语法直接使用&#xff1a; 事件绑定 事件触发 常用事件&#xff1a; 事件对象的属性列表&#xff08;事件回调触发&#xff0c;会收到一个事件对象 event&#xff0c;它的详细属性如下&…

免费,C++蓝桥杯等级考试真题--第10级(含答案解析和代码)

C蓝桥杯等级考试真题--第10级 答案&#xff1a;D 解析&#xff1a;数组是一种线性数据结构&#xff0c;其特点是数组中的元素在内存中占据一段连续的存储空间&#xff0c;每个元素通过索引&#xff08;下标&#xff09;访问&#xff0c;索引起始通常是0。 数组的长度在声明时…

操作符详解

一、移位操作符 1.1左移操作 左边丢弃&#xff0c;右边补0 1.2右移操作 算数右移&#xff1a;右边丢弃&#xff0c;左边补原符号位 逻辑右移&#xff1a;右边丢弃&#xff0c;左边补0 int main() {int a -1;int b a >> 1;printf("b%d\n",b);return 0; } 原码…

厘米级精确定位,开启定位技术新时代

定位技术在当前这个科技发展时代可以说是以以前所未有的速度在发展&#xff0c;其中厘米级精确定位技术更是成为当前的研究热点和实际应用中的佼佼者。这项技术以其高度的精准性和广泛的应用前景&#xff0c;正在逐渐改变我们的生活和工作方式。接下来我们跟着深圳沧穹科技一起…

在vue项目中使用markdown-it回显markdown文本

前言 其实有很多插件都是可以用来回显markdown文本的,这个插件也是其中之一。 文档地址:markdown-it | markdown-it 中文文档 这个文档在vue2和vue3里面都可以使用,所以还是比较推荐的 使用 安装 npm install markdown-it --save 应用 <template><div><…

微服务开发与实战Day02 - Docker

一、Docker快速入门 快速构建、运行、管理应用的工具 安装部署教程&#xff1a;Docs 1. 部署MySQL 测试连接&#xff1a; 镜像和容器 当我们利用Docker安装应用时&#xff0c;Docker会自动搜索并下载应用镜像&#xff08;image&#xff09;。镜像不仅包含应用本身&#xff…

天润融通,荣获2024中国AI应用层创新企业

AI技术发展日新月异&#xff0c;可谓“AI一天&#xff0c;人间一年”。 从2023年到2024年&#xff0c;短短一年的时间&#xff0c;大模型技术的发展就已经逐步从追求“技术突破”转向了追求“应用落地”。如何将大模型的技术与企业的生产、运营、销售等场景结合起来&#xff0…

java版CRM客户关系管理系统源码:CRM客户关系管理系统的功能详解

CRM客户关系管理系统是一款功能全面的客户管理工具&#xff0c;旨在帮助企业和销售团队提高客户管理效率&#xff0c;优化销售流程。该系统包含多个模块&#xff0c;覆盖了从线索到回款的全流程管理&#xff0c;为用户提供了一个集成化的客户关系管理平台。 一、待办事项模块&a…

Docker 部署 Redis Cluster 高性能高可用分片集群

文章目录 1、环境准备2、Cluster 集群讲解2.1、Cluster 介绍2.2、Cluster 和哨兵模式区别2.3、Cluster 如何分散存储数据 3、Cluster 搭建流程3.1、安装 Docker3.2、启动 Redis 容器3.3、创建 Cluster 集群 4、Cluster 集群测试4.1、读写操作4.2、故障转移 1、环境准备 准备6台…

基于Keil5移植LVGL,懂得原理之后什么开发板都可以移植

今天我们来移植一下LVGL&#xff0c;其实LVGL和Qt差不多&#xff0c;操作起来都很简单&#xff0c;看着官方文档都可以自己学习使用。 难就难在移植上面&#xff0c;移植个LVGL花了我三天才弄明白&#xff08;虽然最后发现在一个很弱智的问题上耽误了我两天&#xff09;&#…