SPFA(求最短路径)

适用范围:给定的图存在负权边,这时类似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;
		}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值