spfa(求单源最短路)

一、简介

spfa :Shortest Path Faster Algorithm 

单源最短路:给定一个带权值的有向图,给定图中的一个定点V,则V称为源,V能到达所有的顶点的最短距离称为单源最短路

 

二、图的存储方式

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f 
#define M	500005
using namespace std;
int dis[M],head[M];
int n,m,s,cnt;
bool vis[M];
struct Edge
{//分别为上一条边的位置、这条边的目的地,距离 
	int next,to,dis;
} edge[M]; 

每一次给一条边,分别起点start 、终点end、边权值value,第n次的输入为第n条边

  • n为顶点个数,m为边的个数,s为源,cnt记录目前操作的是第几条边
  • dis[i]数组记录源顶点到i顶点的距离
  • edge[i] 数组记录第i条边的信息,包括这条边的终点to和权值dis, 以及上一次以这条边的起点为起点的边的位置next
  • 用 head[i] 数组记录以i为起点的上一条边在edge数组中的位置
  • vis[i]数组记录顶点i是否在队列中

每次加入一条边的时候都进行建图操作add:


void add(int from,int to,int dis)
{
	cnt++;//目前添加的是第cnt条边 
	edge[cnt].next=head[from];//上一条以from为起点的边的位置 
	edge[cnt].dis=dis;//权值  
	edge[cnt].to=to;//终点 
	head[from]=cnt;//记录最后一次以from为起点的是第几条边 
}

经过这样处理之后,所有源顶点能到达的顶点都可以通过广度优先搜索遍历

 

三、spfa算法

广度优先搜索,记录头顶点u,然后遍历所有u一步能到达的顶点v,比较  S-> v  和  S->u->v  哪条路最短,更新dis[v]

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f 
#define M	500005
using namespace std;
int dis[M],head[M];
int n,m,s,cnt;
bool vis[M];
struct Edge
{//分别为上一条边的位置、这条边的目的地,距离 
	int next,to,dis;
} edge[M]; 

void add(int from,int to,int dis)
{
	cnt++;//目前添加的是第cnt条边 
	edge[cnt].next=head[from];//上一条以from为起点的边的位置 
	edge[cnt].dis=dis;//权值  
	edge[cnt].to=to;//终点 
	head[from]=cnt;//记录最后一次以from为起点的是第几条边 
}

void spfa()
{
	queue<int> Q;
	for(int i=1; i<=n; i++)	dis[i]=inf;//Initialization 

	Q.push(s);//压入初始点 
	dis[s]=0;//自己到自己的距离为0 
	vis[s]=1;//标记 

	while(!Q.empty())//bfs 
	{
		int u=Q.front(),v;
		Q.pop();
		vis[u]=0;
		for(int i=head[u]; i; i=edge[i].next)//便利所有u的出度边 
		{
			v=edge[i].to;//这条边的终点 
			//如果初始点当到达这v的距离小于先到达u在到达v的距离 
			if(dis[v]>dis[u]+edge[i].dis) 
			{
				dis[v]=dis[u]+edge[i].dis;//更新 
				if(vis[v]==0)
				{
					vis[v]=true;//入队 
					Q.push(v);
				}
			}
		}
	}
}
int main()
{
	cin>>n>>m>>s;
	while(m--)
	{
		int start,end,value;
		cin>>start>>end>>value;
		add(start,end,value);
	}
	
	spfa();
	
	for(int i=1;i<=n;i++)
	cout<<dis[i]<<' ';
	
	return 0;
}

 

四、vector优化版

edge[i].next存放的是 i能到达的顶点next ,edge[i].dis存放的是边i->next的权值

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define M 100005
using namespace std;
struct Edge
{//邻接点,权值 
	int next,dis;
};
vector<Edge> edge[M];
int vis[M],dis[M];
int n,m,s;
void add(int from,int to,int dis)
{
	edge[from].push_back((Edge){to,dis});
}
void spfa()
{
	queue<int> Q;
	memset(dis,inf,sizeof(dis));
	Q.push(s);
	dis[s]=0;
	vis[s]=1;
	
	while(!Q.empty())
	{
		int x=Q.front(),v;
		Q.pop();
		vis[x]=false;
		for(int i=0;i<edge[x].size();i++)
		{
			int u=edge[x][i].next, v=edge[x][i].dis;
			if(dis[u]>dis[x]+v)
			{
				dis[u]=dis[x]+v;
				if(!vis[u])
				{
					Q.push(u);
					vis[u]=true;
				}
			}
		}
	}
}
int main()
{
	cin>>n>>m>>s;
	while(m--)
	{
		int x,y,z;
		cin>>x>>y>>z;
		add(x,y,z);
	}
	spfa();
	
	for(int i=1;i<=n;i++)
	cout<<dis[i]<<' ';
	
	return 0;
} 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值