spfa算法:
1.时间复杂度O(kE),k为每个节点进入Queue的次数,且k一般<=2,但此处的复杂度证明是有问题的,其实SPFA的最坏情况应该是O(VE).。相较于dijkstra算法spfa适用于稀疏图,个人经验是spfa算法能处理大多数情况,并且算法比较简单易写,除非题目含有它是一个稠密图的情况。
2.能够计算负权图问题。
3.能够判断是否有负环 (一旦某个点经过了n条边来松弛就说明有负环了或者用一个数组记录各个节点的进队次数,如果大于n也说明有负环)。
算法思想:
1.设立一个先进先出的队列用来保存待优化的结点。
2.优化时每次取出队首结点u,并且用u点当前的最短路径估计值对与u点相邻的结点v进行松弛操作,如果v点的最短路径估计值有所调整,且v点不在当前的队列中,就将v点放入队尾。
3.这样不断从队列中取出结点来进行松弛操作,直至队列空为止 期望的时间复杂度O(ke), 其中k为所有顶点进队的平均次数,可以证明k一般小于等于2。
实现方法:
1.存入图。可以使用链式前向星或者vocter。
2.开一个队列,先将开始的源点加入队列。
3.每次从队列中取出一个节点u,遍历与u相通的v节点,比较dis[v]与dis[u]+(u和v之间的距离),如果dis[v]>dis[u]+(u和v之间的距离),则更新dis[v]。由于改变了dis[v],就有可能影响到其它与v相连的点,所以就需要将v加入队列(如果v已经在队列中了,就不用重复了)期间可以记录这个节点的进队次数,判断是否存在负环。
4.直到队空。
判断有无负环:如果某个点进入队列的次数超过N次则存在负环
这里的vis[]数组和dijkstra算法中的vis[]数组截然不同,这里vis[I]=0表示节点i不在队列中,vis[i]=1,表示节点i在队列中,可能会用到节点i对其他点进行更新。
#include<iostream>
#include<queue>
#include<cstring>
#include<cstdio>
#define inf 99999999
using namespace std;
const int maxn=1005;
int t,n;
int head[maxn],dis[maxn];
bool vis[maxn];
struct edge{
int from,to,w,next;
}e[10000];
int cnt;
void add(int u,int v,int w){
e[cnt].from =u;
e[cnt].to =v;
e[cnt].w =w;
e[cnt].next =head[u];
head[u]=cnt++;
}
void spfa(int s){
queue<int>q;
for(int i=1;i<=n;i++)dis[i]=inf;
memset(vis,0,sizeof(vis));
dis[s]=0;
q.push(s);
while(!q.empty() ){
int u=q.front() ;
q.pop() ;
vis[u]=0;//出队即vis[i]=0;
for(int i=head[u];i!=-1;i=e[i].next ){
int v=e[i].to ;
if(dis[v]>dis[u]+e[i].w ){
dis[v]=dis[u]+e[i].w ;
if(!vis[v]){
vis[v]=1;
q.push(v);
}
}
}
}
}
int main(){
cin>>t>>n;
int u,v,w;
memset(head,-1,sizeof(head));
for(int i=1;i<=t;i++){
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
spfa(1);
printf("%d\n",dis[n]);
return 0;
}
判断是否有负环:
while(!q.empty() ){
int u=q.front() ;
q.pop() ;
vis[u]=0;
for(int i=head[u];i!=-1;i=e[i].next ){
int v=e[i].to ;
if(dis[v]>dis[u]+e[i].w ){
dis[v]=dis[u]+e[i].w ;
if(!vis[v]){
vis[v]=1;
sum[v]++;
if(sum[v]>=n){//有负环
}
else q.push(v);
}
}
}
}