最短路笔记(简单过过)
这个比较简单,但是还是要复习一下的
目录
- 什么是图
- 最短路算法
- 各个算法的复杂度
- 相关题目
1.0什么是图
1.1图的定义
图(Graph):是由一种由顶点(Vertex)非空有限集合和顶点之间边(Edge)的集合组成的数据结构,表示为G(V,E)。
其中,G为图,V为顶点集合,E为边的集合。顶点集合有穷非空,边集合可以为空。
1.2图的分类
简单来说也就两种:有向图和无向图。
2.0最短路算法
2.1 Floyd算法
时间复杂度 O(n3)。
这个算法其实就是一个大暴力,
在题目数据范围 ≤500的情况下,建议使用。(毕竟代码量少啊)
算法思路
对于两个点i,j,两个点的最短路为disij
从1到n枚举一个k,
如果disik+diskj<disij (即让k为一个中间商一样的东西)
那我们就让disij更新。
代码实现
void floyd()
{
for(int k=1;k<=n;k++)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
}
}
}
2.2 DijkStra算法
时间复杂度:O(n2)(可优化到O(n log n))
可以说是最常用的了,因为优化后很稳定。不像SPFA
注意:有负权值时不得使用(SPFA上场了)
算法思路
每次找到离源点最近的一个顶点,然后以该顶点为中心进行扩展,最终得到源点到其余所有点的最短路径。

借用了AW顿顿的一幅图,别介意qwq
举个例子

求A到E的最短距离。
答案是:11。
个人感觉这种手算用spfa不容易错,但dij更容易些吧。
过程:
- 一开始,disB=3,disF=6,disG=4
- 找A相邻的最短边且没遍历过的边,即B,那disC=4,disD=10。
- 找B相邻的最短边且没遍历过的边,即C,那disE=12,disF=5。
- 找C相邻的最短边且没遍历过的边,即F,那disE=11,disD=7。
(由于作者懒,已经推出了正确路径后,后面的模拟就懒得做了,于是不写了)
代码实现
普通版本
#include<bits/stdc++.h>
#define MAXN 0x7fffffff
using namespace std;
int dist[5001],n,m,edge[5001][5001],minn,idx,s;
bool vis[5001];
void dijkstra(){
for(int j=1;j<n;++j){
minn=MAXN;
for(int i=1;i<=n;i++){
if(!vis[i]&&dist[i]<minn){
minn=dist[i];
idx=i;
}
}
vis[idx]=1;
for(int i=1;i<=n;i++){
if(edge[idx][i]!=MAXN){
int t=edge[idx][i]+dist[idx];
if(dist[i]>t)dist[i]=t;
}
}
}
}
int main()
{
cin>>n>>m>>s;
for(int i=1;i<=n;i++)for(int j=1;j<=n;++j){
if(i!=j)edge[i][j]=MAXN;
}
for(int i=0;i<m;i++){
int x,y,len;
cin>>x>>y>>len;
edge[x][y]=len;
}
for(int i=1;i<=n;++i){
dist[i]=edge[s][i];
}
vis[s]=1;
dijkstra();
for(int i=1;i<=n;i++)cout<<dist[i]<<' ';
return 0;
}
优化版本(用了简单的优先队列)
#include<bits/stdc++.h>
using namespace std;
#define maxn 100050
#define maxm 500005
#define INF 1234567890
struct Edge
{
int u,v,w,next;
}e[maxm];
int head[maxn],cnt,n,m,s,vis[maxn],dis[maxn];
struct node
{
int w,now;
inline bool operator <(const node &x)const
{
return w>x.w;
}
};
priority_queue<node>q;
inline void add(int u,int v,int w)
{
e[++cnt].u=u;
e[cnt].v=v;
e[cnt].w=w;
e[cnt].next=head[u];
head[u]=cnt;
}
void dijkstra()
{
for(int i=1;i<=n;i++)
{
dis[i]=INF;
}
dis[s]=0;
q.push((node){0,s});
while(!q.empty())
{
node x=q.top();
q.pop();
int u=x.now;
if(vis[u]) continue;
vis[u]=1;
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].v;
if(dis[v]>dis[u]+e[i].w)
{
dis[v]=dis[u]+e[i].w;
q.push((node){dis[v],v});
}
}
}
}
int main()
{
cin>>n>>m>>s;
for(int i=1,x,y,z;i<=m;i++)
{
cin>>x>>y>>z;
add(x,y,z);
}
dijkstra();
for(int i=1;i<=n;i++)
{
printf("%d ",dis[i]);
}
return 0;
}
2.3 SPFA算法
它死了
时间复杂度:O(nm)(最坏情况,即菊花图)
但它可以实现有负权值的情况。
算法思路
大概思路就是从起点出发,每个点(除了起点)到起点的距离设为inf
此时将起点入队(queue)
用while语句当队列不为空时
每次提取队头元素(x),
找到队头相邻的点(y),
接着作比较if(disx+exy<disy)(eij表示i到j的边长)
如果disy发生了改变,
并且y不在队列,那就把y放入队列。
代码实现
#include<iostream>
#include<algorithm>
#include<iomanip>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define INF 999999999
using namespace std;
const int N=4e6+50;
int n,m,x,y,tot,f[N],bo[N],e[N],b;
long long dis[N];
struct edge
{
int from,v,w,nxt;
}a[N];
void add(int ue,int ve,int we)
{
a[++tot].nxt=f[ue];
a[tot].from=ue;
a[tot].v=ve;
a[tot].w=we;
f[ue]=tot;
}
void spfa(int u)
{
queue<int>que;
for(int i=1;i<=n;i++)
{
e[i]=0;
bo[i]=0;
dis[i]=INF;
}
dis[u]=0;
que.push(u);
e[u]=1;
bo[u]=1;
while(!que.empty())
{
int x=que.front();
que.pop();
bo[x]=0;
for(int i=f[x];i!=0;i=a[i].nxt)
{
// cout<<x<<" "<<y<<endl;
int y=a[i].v;
// cout<<x<<" "<<y<<endl;
if(dis[y]>dis[x]+a[i].w)
{
dis[y]=dis[x]+a[i].w;
if(bo[y]==0)
{
que.push(y);
bo[y]=1;
e[y]++;
if(e[y]>=n)
return;
}
}
}
}
}
int main()
{
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
cin>>n>>m;
for(int i=1;i<=m;i++)
{
cin>>b>>x>>y;//x,y为结点,b为边权
add(x,y,b);
add(y,x,b);
}
spfa(1);
cout<<dis[n]<<endl;
return 0;
}
注:如果SPFA被卡,可以用duque优化。
3.0各个算法的复杂度
Floyd:O(n3)
Dijkstra:O(n log n)(优化后的)
SPFA:O(mn)
4.0相关题目

浙公网安备 33010602011771号