前言:存在负权边时dijkstra算法不可使用,存在无限死循环,因此引入spfa算法。
详解:spfa求解单源最短路,能进行负权环的判断,存在负权环不能输出最短路,反之可行。
实现方法:建立一个队列,初始时队列里只有起始点,然后执行松弛操作,用队列里有的点去刷新起始点到所有点的最短路,如果刷新成功且被刷新点不在队列中则把该点加入到队列最后。重复执行直到队列为空。
时间复杂度O(MN)
本博客主要以知识点复习为主
#include<bits/stdc++.h>
using namespace std;
const int Max=100; //点的数目
const int INF=1e9; //设置最大值
struct node
{
int to;
int val;
};
node temp;
queue<int>check; //存储点集
vector<node>edge[Max]; //记录边的数据
int dis[Max]; //起点到达各点最短路
int vis[Max]; //记录点是否在队列里
int cnt[Max]; //记录点被压进队列次数
int path[Max]; //记录路径
int n,m; //点与边数目
int flag; //设置负环是否存在
void init() //初始化路径
{
for(int i=0;i<Max;i++)
{
edge[i].clear();
dis[i]=INF;
path[i]=-1;
cnt[i]=0;
}
while(check.empty()==0)
{
check.pop();
}
}
void read() //读入数据
{
int u,v,w;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&u,&v,&w);
temp.val=w;
temp.to=v;
edge[u].push_back(temp);
temp.val=w;
temp.to=u;
edge[v].push_back(temp);
}
}
int spfa(int s) //更新最小路径
{
check.push(s);
dis[s]=0;
vis[s]=1;
cnt[s]=1; //设置起点入队列次数
path[s]=-1;
while(check.empty()==0)
{
int temp=check.front();
int cub=edge[temp].size();
for(int i=0;i<cub;i++)
{
node t=edge[temp][i];
if(dis[temp]+t.val<dis[t.to])
{
path[t.to]=temp;
dis[t.to]=dis[temp]+t.val;
if(vis[t.to]==0) //判断是否已经入列
{
check.push(t.to);
vis[t.to]=1;
cnt[t.to]++;
if(cnt[t.to]>n-1) //存在负环退出
{
return 0;
}
}
}
}
check.pop();
vis[temp]=0;
}
return 1;
}
void output(int e) //打印路径
{
if(path[e]==-1) //路径起始点
{
printf("%d",e);
}
else
{
output(path[e]);
printf("->%d",e);
}
}
int main()
{
while(scanf("%d%d",&n,&m)!=-1)
{
init();
read();
flag=spfa(1); //以起点1为例
if(flag==0)
{
printf("存在负环\n");
}
else
{
printf("%d\n",dis[n]); //以终点n为例
output(n);
printf("\n");
}
}
return 0;
}