SPFA可能会被卡掉,能用dijkstra就别用SPFA,代码较长,但我已尽力做到解释,请耐心看下去,存储为邻接表存储。
#include<bits/stdc++.h> #define inf 0x3f3f3f3f//(宏定义一个很大的值,例如0x3f3f3f3f等) using namespace std; int n,m,cnt;//cnt 计数器(有cnt条边) struct edge//结构体定义 { int v,w,nxt;//v 目标点 w 边权 nxt 这条边的上一条边(遍历) }; edge e[3000010];//存边(边表) int dis[3001000];//记录起点到第x个点的距离 int h[1001000];//记录第x个点所发出的最后一条边 bool f[1001000];//判断是否在队列内 deque<int> q;//双端队列 void add(int,int,int); void spfa()//SLF优化 { for(int i=1;i<=n;++i) { dis[i]=inf; }//初始化,也可以用memset() dis[1]=0;//起点到自己的距离为0 (该题起点为1) q.push_back(1);//起点入队 f[1]=1;//标志起点已入队 while(!q.empty()) { int top=q.front();//取队首元素 q.pop_front();//踢出队列 int w=dis[top];//w 起点的值 f[top]=0;//代表该元素已出队 for(int i=h[top];i;i=e[i].nxt)//邻接表遍历每一条边 { int v=e[i].v;//目标点 int di=e[i].w;//边权 if(dis[v]>w+di)//松弛操作 { dis[v]=w+di;//更新起点到点v的值 if(!f[v])//判断是否入队,没入队便入队 { if(!q.empty()&&dis[v]<dis[q.front()])//如果比队首元素小,从队首入队 {//这里一定要把!q.empty()放在前面,编译时它会从前往后读,如果把它放后面而队列又为空,dis[v]<dis[q.front()]调用时会RE q.push_front(v);//入队操作 } else q.push_back(v);//否则从队尾入队 f[v]=1;//标志已入队 } } } } } int main() { memset(h,0,sizeof h);//数组初始化 memset(f,false,sizeof f);//数组初始化 scanf("%d%d",&n,&m); for(int a,b,c,i=1;i<=m;++i) { scanf("%d%d%d",&a,&b,&c); add(a,b,c); //加边 add(b,a,c);//加边,如果是有向图,删掉这一步操作 } spfa();//调用函数 printf("%d",dis[n]);//输出,根据题目要求,这里是输出1到n的最短距离 return 0; } void add(int u,int v,int w) { e[++cnt].v=v;//cnt 边的编号 e[cnt].w=w; e[cnt].nxt=h[u];//指向上一条边 h[u]=cnt;//更新最后一条边 }
2022.7.8更新:
新增判断负环的写法和dfs写法
dfs写法判断负环更快,但求答案很慢
bfs队列写法求答案快,但判断负环很慢
根据题目情况来
bfs队列写法:
bool spfa() { for(rint i=1;i<=n;++i) dis[i]=0x7ffffff; dis[0]=0; q.push_back(0); vis[0]=true; ++in[0];//差记录入队次数 while(!q.empty()) { int u=q.front(); q.pop_front(); vis[u]=false; for(rint i=h[u];i;i=e[i].nxt) { int v=e[i].v;ll w=e[i].w; if(dis[v]>dis[u]+w) { dis[v]=dis[u]+w; if(!vis[v]) { ++in[v]; if(in[v]>=n+1) return false;//判断负环 if(!q.empty()&&dis[v]<dis[q.front()]) q.push_front(v); else q.push_back(v); vis[v]=true; } } } } return true; }
dfs写法:
void dfs_spfa(int u) { if(fg) return; vis[u]=true; for(rint i=h[u];i;i=e[i].nxt) { int v=e[i].v; ll w=e[i].w; if(dis[v]>dis[u]+w) { dis[v]=dis[u]+w; if(vis[v]==true)//如果这个点被访问过,就说明这是负环 { fg=true;//打标记 return; } else dfs_spfa(v); } } vis[u]=false; }