全局准备工作
int N, X; //N为点数 X为源点
int head[MAXN]; //head[src]表示以head为出发点的邻接表表头在数组Adj中的位置,开始时所有元素初始化为-1
int nodeP; //在邻接表和指向表头的head数组中定位用的记录指针,开始时初始化为0
int dist[MAXN]; //储存到源节点的距离,在Spfa()中初始化
bool vis[MAXN]; //这里vis作inqueue解释会更好,出于习惯使用了vis来命名,在Spfa()中初始化
Node定义
struct Node {
int v, w, next;
}Adj[MAXM];
v即vertex,这里意思相当于被指向的点to,w相当于weight,代表边权,next用来指向在Adj中下一个从相同src出发的边指向的点
addEdge函数
void addEdge(int src, int to, int weight)
{
Adj[nodeP].v = to;
Adj[nodeP].w = weight;
Adj[nodeP].next = head[src];
head[src] = nodeP++;
}
addEdge(a, b, w)作用是在数组Adj中留下一条记录了去向和边权的记录,且其next指向表中相同src指向的下一个点,同时更新了head表头
Spfa主体
void Spfa()
{
queue que;
int i;
for(i = 1; i <= N; i++) {
dist[i] = NIL;
vis[i] = false;
}
dist[X] = 0; //X为源点
que.push(X);
while(!que.empty()) {
int now = que.front();
que.pop();
vis[now] = false; //从queue中退出
//遍历邻接表
for(i = head[now]; i != -1; i = Adj[i].next) { //在Adj中,相同src出发指向的顶点为从head[src]开始的一项,逐项使用next寻找下去,直到找到第一个被输
//入的项,其next值为-1
int to = Adj[i].v;
if(dist[to] == NIL || dist[to] > dist[now] + Adj[i].w) { //松弛(RELAX)操作
dist[to] = dist[now] + Adj[i].w;
if(!vis[to]) { //若被搜索到的节点不在队列que中,则把to加入到队列中去
vis[to] = true;
que.push(to);
}
}
}
}
}
main中初始化
int main()
{
memset(head, -1, sizeof(head));
...
while(...) {
cin >> a >> b >> c;
addEdge(a, b, c);
}
Spfa();
//结果已经储存在dist数组中
return 0;
}
有些人以为这个算法是Dijkstra,事实上Spfa和Dijkstra很像,只是他们维护的队列不同,Spfa维护的是一个初始只有源节点的队列,而Dijkstra要维护初始为所有顶点的队列。