图当中的最短路算法,主要分为单源最短路径与多源最短路径,本文分别讲解一种经典算法
单源最短路径——dijkstra
算法思路
单源最短路径意为从某个点到其余点的最短路径(权值最小)
算法本质为贪心思想,考虑局部最优。每次找到路径最短的节点标记走过
在每一次取最小节点时用路过该节点的路径更新存着最短路径的数组。
基础步骤
1、邻接表或链式前向星存图
2、重点:定义数组d[i],表示定点s到点i的最短路径
3、进行n-1次查询,每次找到d[x]最小值的x进行标记,遍历x的子节点,更新d数组(经过点x)
时间复杂度分析:n-1次查询,每次找最小值,时间复杂度
但这样还不够!
不够快!
显然找最大值可以用堆优化
于是nlogn的代码就应运而生啦
小tips:取相反数存入实现小根堆哦
代码
#include<bits/stdc++.h>
using namespace std;
int n,m,s,d[314514];
int head[114514],idx,vis[214514];
struct edge{
int v,next,w;
}edg[214514];
void add(int u,int v,int w){
edg[++idx].w=w;
edg[idx].v=v;
edg[idx].next=head[u];
head[u]=idx;
}//链式前向星存图
priority_queue< pair<int,int> > q;//堆
void dj(){
memset(d,0x3f,sizeof(d));//方便取min
d[s]=0;//初始点标为0
q.push(make_pair(0,s));//初始化
while(q.size()){
int fa=q.top().second;//找到d数组最小值
q.pop();//弹出
if(vis[fa]) continue;//判断是否到过
vis[fa]=1;//标记
for(int i=head[fa];i;i=edg[i].next){//遍历子节点
int y=edg[i].v,wy=edg[i].w;
if(d[y]>d[fa]+wy) {//更新d数组,从节点fa到y的新路径
d[y]=d[fa]+wy;
q.push(make_pair(-d[y],y));//存入堆,注意取相反数实现小根堆
}
}
}
}
int main()
{
cin>>n>>m>>s;
for(int i=1;i<=m;i++){
int a1,a2,a3;
cin>>a1>>a2>>a3;
add(a1,a2,a3);
}
dj();
for(int i=1;i<=n;i++) cout<<d[i]<<" ";
return 0;
}
多源最短路径——Floyd
算法思路
意为从任意点到任意点的最短路径
算法基于简单动态规划,定义f[k][i][j]表示考虑前k个节点,从i到j的最短路径
与背包问题类似,在从小到大的循环中,k可以舍去,故f数组空间优化成了二维
为了满足dp从部分推广至全局的思路,借鉴区间dp,枚举i到j的中间点k,就易得状态转移方程
时间复杂度 n^3
算法简单,贴贴代码吧
代码
#include<bits/stdc++.h>
using namespace std;
int n,a[114][4600],m,u,v,w;
int main(){
cin>>n>>m;
memset(a,0x3f,sizeof(a));
for(int i=1;i<=m;i++){
cin>>u>>v>>w;
a[u][v]=min(a[u][v],w);
}
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if((a[i][k]+a[k][j]<a[i][j])){
a[i][j]=a[i][k]+a[k][j];
}
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cout<<a[i][j]<<" ";
}
cout<<endl;
}
return 0;
}