SPFA算法
(本文中的图片来源于博友的博客,图中有水印)
作用:找最短路径
说明:是dijkstra的优化,动态寻找某个点到其它点的最短路径,尤其可以处理有向负权值
算法:
1.选取一个起点S,设定一个记录S到其它所有点距离的数组并初始化,操作如下:
①设定地图
②设定距离数组(S,S)=0,(S,Ki≠S)=inf
③设定处理队列{S}
2.对队列中的每一个点Ki进行以下操作:
①取出队首的点(初始时只有起点)
②对取出的点邻近的目标点进行分析,如果找到了更优路径(min(S,Ki)+ map(Ki,Kj)<min(S,Kj)):
(1)更新起点S到该目标点Kj的最短距离
(2)如果目标点已经在队列中,不再入列,否则入列
③检查队列中是否还有元素,如果有,返回步骤①,否则结束
3.距离数组中的值就是需要的S到各点的最短路程
操作:
如图,我们要找这个图的v1到其它点的最短路径
初始的数组记录如下:
处理队列:{v1}
从v1开始搜索可能的路径,更新如下:
此时,v1处理完毕,出列,而v3、v5、v6没有处理,入列(有点像BFS)
处理队列:{v3,v5,v6}
接下来处理v3,v3和上述点暂时还没有关联,继续更新:
处理队列:{v5,v6,v4}
然后处理v5,v5的处理涉及到v4和v6,当然也涉及到v1最短路径可能要修改
v5到v6的距离是60,而v4和v6在表中已经有值了,这时就要多一个操作,那就是比较(v1,v5)+(v5,vi)和表中已经存储的(v1,vi),将更短的哪一个存下来,于是更新如下:
上一步操作涉及到了v4和v6,它们已经在队列中,不需要重复入列
处理队列:{v6,v4}
再处理v6,没有可走路径,直接出列
处理队列:{v4}
v4到v6长度为10,比较10+50和90,发现(v1,v4,v6)是一个更好的选择,更新如下:
v6被更新了,所以v4出列,v6入列!
处理队列:{v6}
同样的,v6没有路可走,那么直接出列,队列空了,v1为源的遍历完成。其中v2不可达。
实现:
void spfa()
{
//我们需要的是从s到其它任何一点的距离,其中的u是当前处理点,v是邻近目标点
memset(dis,0x7f,sizeof(dis)); //dis数组用存放问询的起点到各点的距离
dis[s]=0;
memset(exist,false,sizeof(exist)); //exist数组用于记录点是否在队列中
team[1]=s; //队列数组
head=0; //队首指针
tail=1; //队尾指针
exist[s]=true;//以上为spfa初始化
do
{
++head;
u=team[head];
exist[u]=false;//取出一个点
for(v=1;v<=n;++v)
{
if(dis[v]>dis[u]+g[u][v]) //边的比较操作,g是地图
{
dis[v]=dis[u]+g[u][v];//更新
if(!exist[v])
{
++tail;
team[tail]=v;
exist[v]=true;//如果被更新的点没在队列里就入队
}
}
}
}while(head<tail); //队列不为空
}
实战
https://www.luogu.org/problemnew/show/P2683
#include<iostream>
#include<queue>
#define inf 999999999
using namespace std;
queue<long int>deal;
long int map[102][102];
long int dis[102][102];
long int flag[102]; // save whether a point is in the queue
int N, M;
void init() {
for (long int i = 0;i <= 101;i++) {
flag[i] = 0;
for (long int j = 0;j <= 101;j++) {
map[i][j] = 0;
dis[i][j] = 0;
}
}
return;
}
long int ask(long int a,long int b) {
//spfa
deal.push(a);
flag[a] = 1;
while (deal.empty() == false) {
long int obj = deal.front();
flag[obj] = 0;
deal.pop();
for (int i = 1;i <= N;i++) {
if (dis[a][obj] + map[obj][i] < dis[a][i]) { // it is a better way
dis[a][i] = dis[a][obj] + map[obj][i];
//cout << "Find a better way!" << endl;
if (flag[i] == 0) { //need to deal
flag[i] == 1;
deal.push(i);
}
}
}
}
return dis[a][b];
}
int add(long int a, long int b, long int w) {
//set map
if (w < map[a][b]) {
map[a][b] = w;
map[b][a] = w;
}
return 0;
}
int main() {
init(); //prepare map and dis
cin >> N >> M;
for (int i = 1;i <= N;i++) { //init map and dis
for (int j = 1;(j <= N);j++) { // self distance is still 0
if (j == i) { continue; }
map[i][j] = inf;
}
}
for (int i = 1;i <= M;i++) {
for (int k = 1;k <= N;k++) { //init map and dis
for (int j = 1;(j <= N);j++) { // self distance is still 0
if (j == k) { dis[k][j] = 0; continue; }
dis[k][j] = inf;
}
}
int command;
cin >> command;
if (command == 0) { //ask
long int a, b;
cin >> a >> b;
long int result = ask(a, b);
if (result == inf) { cout << "-1" << endl; }
else { cout << result << endl; }
}
else { //add
long int a, b, w;
cin >> a >> b >> w;
add(a, b, w);
}
}
return 0;
}