SPFA是非常简单的最短路径算法,思想就是从起点开始,进行宽度优先搜索,不断松弛S点到其他相邻点的距离。如果松弛了点B,则把点B放到队列里。假如点B已经在队列里了,就不要放了,判断在不在队列可以用个数组来表示。
引用一段hihocoder上的解释:
构造一个队列,最开始队列里只有(S, 0)——表示当前处于点S,从点S到达该点的距离为0,然后每次从队首取出一个节点(i, L)——表示当前处于点i,从点S到达该点的距离为L,接下来遍历所有从这个节点出发的边(i, j, l)——表示i和j之间有一条长度为l的边,将(j, L+l)加入到队尾,最后看所有遍历的(T, X)节点中X的最小值就是答案咯~”小Ho对于搜索已经是熟稔于心,张口便道。
SPFA算法呢,其实某种意义上就是宽度优先搜索的优化——如果你在尝试将
(p, q)
加入到队尾的时候,发现队列中已经存在一个(p, q‘)
了,那么你就可以比较q和q’:如果q>=q‘
,那么(p, q)
这个节点实际上是没有继续搜索下去的必要的——算是一种最优化剪枝吧。而如果q < q‘
,那么(p, q‘)
也是没有必要继续搜索下去的——但是它已经存在于队列里了怎么办呢?很简单,将队列中的(p, q‘)
改成(p, q)
就可以了!”“那我该怎么知道队列中是不是存在一个
(p, q‘)
呢?”维护一个
position[1..N]
的数组就可以了,如果不在队列里就是-1,否则就是所在的位置!”“所以说这本质上就是宽度优先搜索的剪枝咯?”小Ho问道。
小Hi笑道:“怎么可能!SPFA算法其实是BELLMAN-FORD算法的一种优化版本,只不过在成型之后可以被理解成为宽度优先搜索的!这个问题,我们会在之后好好讲一讲的!
代码如下:
#include <iostream>
#include <cstring>
#include <string>
#include <queue>
using namespace std;
struct Node{
int val;
int len;
Node * next;
Node():val(-1),len(-1), next(NULL){}
Node(int x, int le): val(x), len(le), next(NULL){}
};
long long SPFA(Node node[], int M, int S, int T){
queue<int> st;
st.push(S);
int flag[M+5];
long long dist[M+5];
for(int i = 0; i < M+5; i++){
flag[i] = 0;
dist[i] = -1;
}
dist[S] = 0;
flag[S] = 1;
while(!st.empty()){
int cur = st.front();
st.pop();
flag[cur] = 0;
Node * temp = node[cur].next;
while(temp != NULL){
if(dist[temp->val] == -1 || dist[temp->val] > (dist[cur]+temp->len)){
dist[temp->val] = dist[cur]+temp->len;
if(flag[temp->val] == 0){
st.push(temp->val);
flag[temp->val] = 1;
}
}
temp = temp->next;
}
}
return dist[T];
}
int main(){
int M,N,S,T;
// M 顶点个数, N 边个数, S起点, T 终点
while(cin>>M>>N>>S>>T){
Node node[M+5];
int st, en, le;
for(int i = 0; i < N;i++){
cin>>st>>en>>le;
//st边起点 en边终点 le边长度
Node * temp = new Node(en,le);
temp->next = node[st].next;
node[st].next = temp;
/*如果是无向图加上这三条语句
temp = new Node(st,le);
temp->next = node[en].next;
node[en].next = temp;
*/
}
cout<<SPFA(node, M, S, T)<<endl;
}
}
大致如上
时间: 2024-11-03 10:34:56