超级传送门 :
http://acm.hdu.edu.cn/showproblem.php?pid=1874
题目大意:
给出N个点,M条路,都是双向连通的,M条路的长度,求A点到B点的最短路径距离。
题目分析:
很朴素的最短路径的题目,用Dijkstra就能过了,因为路不带负权,但是小菜想学一下SPFA,就用了。
求单源最短路的SPFA算法的全称是:Shortest Path Faster Algorithm。 (我很喜欢这个名字(*^__^*) )
下面简单陈述一下这个算法的步骤吧。。。
开一个与顶点同样的的数组dis[],存放距离;map[][]地图数组;visited[]记录检查次数;
ps: 出入队列都要让visited 加1;
1 初始化,dis全部初始化成非常大,把源点的dis初始化为0; 源点进队列;
2 循环: (取队头元素u) 找出所有和u相连的v ,松弛 边(u,v) ,如果能松弛 ,而且队里面没有v,则v进队
2.1 松弛 : 如果dis[u] + map[u][v] > dis[v] 则可以松弛,并且松弛了他,dis[v] = dis[u] + map[u][v]
2.2 因为出对入队visited都加1,所以当visited 为奇数时,v在队列里面;
3 循环结束条件: 1,队列为空,则已经得出源点到其他全部点的最短路径,
2 任一点的visited大于等于顶点数N*2(N入队大于等于N次) 则表示有负环(负开销回路),这个图没有最短路径
大致步骤如上,恕我不能给出证明。。。
代码如下实现:
#include<iostream>
#include<cstring>
#include<stdio.h>
#include<queue>
using namespace std;
#define inf 0X3f3f3f3f
const int maxV = 220;
int map[maxV][maxV];
int dis[maxV];
int visited[maxV];
int N,M;
int release(int v1 , int v2)
{
if(dis[v1] + map[v1][v2] < dis[v2])
{
dis[v2] = dis[v1] + map[v1][v2];
return 1;
}
return 0;
}
int bellman(int src)
{
int i,j;
int temp;
queue<int> q;
//初始化距离
for(i=0;i<=N;i++)
{
dis[i] = inf;
}
dis[src] = 0;
visited[src] = 1;
q.push(src);
while(!q.empty())
{
temp = q.front();
visited[temp]++;
q.pop();
for(i=0;i<N;i++)
{
if(map[temp][i] != -1)
{
if(visited[i] >= maxV*2) return 0;//有负环
if( release(temp,i) == 1)
{
if(visited[i] % 2 == 0)
{
q.push(i);
visited[i]++;
}
}
}
}
}
return 1;
}
int main()
{
int i,J;
int a,b,c;
int S,T;
while(scanf("%d %d",&N,&M) != EOF)
{
memset(map,255,sizeof(map));
memset(visited,0,sizeof(visited));
for(i=0;i<M;i++)
{
scanf("%d %d %d",&a,&b,&c);
if(map[a][b] == -1 || map[a][b] > c)
{
map[a][b] = c;
map[b][a] = c;
}
}
scanf("%d %d",&S,&T);
if(bellman(S) == 1)
{
if(dis[T] != inf)
{
printf("%d\n",dis[T]);
}
else
{
printf("-1\n");
}
}
}
return 0;
}