一、简介
spfa :Shortest Path Faster Algorithm
单源最短路:给定一个带权值的有向图,给定图中的一个定点V,则V称为源,V能到达所有的顶点的最短距离称为单源最短路
二、图的存储方式
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define M 500005
using namespace std;
int dis[M],head[M];
int n,m,s,cnt;
bool vis[M];
struct Edge
{//分别为上一条边的位置、这条边的目的地,距离
int next,to,dis;
} edge[M];
每一次给一条边,分别起点start 、终点end、边权值value,第n次的输入为第n条边
- n为顶点个数,m为边的个数,s为源,cnt记录目前操作的是第几条边
- dis[i]数组记录源顶点到i顶点的距离
- edge[i] 数组记录第i条边的信息,包括这条边的终点to和权值dis, 以及上一次以这条边的起点为起点的边的位置next
- 用 head[i] 数组记录以i为起点的上一条边在edge数组中的位置
- vis[i]数组记录顶点i是否在队列中
每次加入一条边的时候都进行建图操作add:
void add(int from,int to,int dis)
{
cnt++;//目前添加的是第cnt条边
edge[cnt].next=head[from];//上一条以from为起点的边的位置
edge[cnt].dis=dis;//权值
edge[cnt].to=to;//终点
head[from]=cnt;//记录最后一次以from为起点的是第几条边
}
经过这样处理之后,所有源顶点能到达的顶点都可以通过广度优先搜索遍历
三、spfa算法
广度优先搜索,记录头顶点u,然后遍历所有u一步能到达的顶点v,比较 S-> v 和 S->u->v 哪条路最短,更新dis[v]
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define M 500005
using namespace std;
int dis[M],head[M];
int n,m,s,cnt;
bool vis[M];
struct Edge
{//分别为上一条边的位置、这条边的目的地,距离
int next,to,dis;
} edge[M];
void add(int from,int to,int dis)
{
cnt++;//目前添加的是第cnt条边
edge[cnt].next=head[from];//上一条以from为起点的边的位置
edge[cnt].dis=dis;//权值
edge[cnt].to=to;//终点
head[from]=cnt;//记录最后一次以from为起点的是第几条边
}
void spfa()
{
queue<int> Q;
for(int i=1; i<=n; i++) dis[i]=inf;//Initialization
Q.push(s);//压入初始点
dis[s]=0;//自己到自己的距离为0
vis[s]=1;//标记
while(!Q.empty())//bfs
{
int u=Q.front(),v;
Q.pop();
vis[u]=0;
for(int i=head[u]; i; i=edge[i].next)//便利所有u的出度边
{
v=edge[i].to;//这条边的终点
//如果初始点当到达这v的距离小于先到达u在到达v的距离
if(dis[v]>dis[u]+edge[i].dis)
{
dis[v]=dis[u]+edge[i].dis;//更新
if(vis[v]==0)
{
vis[v]=true;//入队
Q.push(v);
}
}
}
}
}
int main()
{
cin>>n>>m>>s;
while(m--)
{
int start,end,value;
cin>>start>>end>>value;
add(start,end,value);
}
spfa();
for(int i=1;i<=n;i++)
cout<<dis[i]<<' ';
return 0;
}
四、vector优化版
edge[i].next存放的是 i能到达的顶点next ,edge[i].dis存放的是边i->next的权值
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define M 100005
using namespace std;
struct Edge
{//邻接点,权值
int next,dis;
};
vector<Edge> edge[M];
int vis[M],dis[M];
int n,m,s;
void add(int from,int to,int dis)
{
edge[from].push_back((Edge){to,dis});
}
void spfa()
{
queue<int> Q;
memset(dis,inf,sizeof(dis));
Q.push(s);
dis[s]=0;
vis[s]=1;
while(!Q.empty())
{
int x=Q.front(),v;
Q.pop();
vis[x]=false;
for(int i=0;i<edge[x].size();i++)
{
int u=edge[x][i].next, v=edge[x][i].dis;
if(dis[u]>dis[x]+v)
{
dis[u]=dis[x]+v;
if(!vis[u])
{
Q.push(u);
vis[u]=true;
}
}
}
}
}
int main()
{
cin>>n>>m>>s;
while(m--)
{
int x,y,z;
cin>>x>>y>>z;
add(x,y,z);
}
spfa();
for(int i=1;i<=n;i++)
cout<<dis[i]<<' ';
return 0;
}