适用范围:给定的图存在负权边,这时类似Dijkstra等算法便没有了用武之地,而Bellman-Ford算法的复杂度又过高,SPFA算法便派上用场了。 我们约定有向加权图G不存在负权回路,即最短路径一定存在。当然,我们可以在执行该算法前做一次拓扑排序,以判断是否存在负权回路,但这不是我们讨论的重点。
模板:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <vector>
#define For(a,b) for(int a=0;a<b;a++)
#define mem(a,b) memset(a,b,sizeof a)
using namespace std;
typedef long long ll;
const int maxn = 1e3+5;
const int INF = 0x3f3f3f3f; //无穷大
const int inf = 0x3f;
const double PI = acos(-1);
struct edge {
int e,w;
edge(int _e,int _w) { //构造函数,使用edge(a,b) 可以直接构造一个e = a,w = b结构体
e = _e,w = _w;
}
//如果需要申请结构体变量,还需要加上下面这个构造函数
//edge(){};(对没错里面都是空的)
};
vector<edge> Map[maxn]; //邻接表存图
bool vis[maxn]; //该点是否在队列中
int dis[maxn]; //初始点到每点的最短距离
int n,m;
void spfa(int a) {
memset(dis,inf,(n+1)<<2); //将数组中前n+1个数初始化为无穷大
mem(vis,0);
dis[a] = 0;
vis[a] = true;
/*****************上面都是和Dijkstra一样的,接下来才是重头******************/
/**Dijkstra:每次找权值最小的边,然后根据这个边所连的点更新dis数组
所以每n次操作只能找出一个点来
SPFA:如果该边可以松弛且连接的点未被使用,就把该边连接的那个点存进队列等待更新
所以每经过一个点就可以把该点所有边所连接的点(可以被松弛的)都记录然后更新
*/
queue<int>q;
q.push(a);
while(!q.empty()) {
int buf = q.front(); //取出队首的点
q.pop();
vis[buf] = 0;
For(i,Map[buf].size()) {
int p = Map[buf][i].e; //取出队首的点
int w = Map[buf][i].w;
if(dis[buf]+w < dis[p]) { //如果可以松弛就更新dis数组,此处和Dijkstra一样
dis[p] = dis[buf]+w; //松弛的同时如果该点未被访问就把该点扔进队列
if(!vis[p]) { //不同的地方,如果p点未被使用,则将其放入队列(也就是p点可以被松弛)
//num[p]++;
//if(num[p]>n+1)
//return false;
q.push(p);
vis[p] = 1;
}
}
}
}
//return true;
}
int main() {
int p1,p2,w;
while(cin >> n >> m) {
if(!n&&!m)
break;
For(i,n)
Map[i].clear();
For(i,m) {
cin >> p1 >> p2 >> w;
//构建无向图
Map[p1].push_back(edge(p2,w));
Map[p2].push_back(edge(p1,w));
}
spfa(1);
cout << dis[n] <<endl;//int dis[maxn]; //初始点到每点的最短距离;
}
return 0;
}