最短路径算法:Floyd Dijkstra SPFA Bellman-ford 从入门到入坑 完整版

本文深入讲解了Dijkstra、Bellman-Ford、SPFA和Floyd四种经典的图算法,包括各自的适用场景、算法思想、时间复杂度及代码实现,并提供了丰富的例题解析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

何时用:Dijkstra算法,Bellman Ford算法,SPFA算法,Floyd算法

在这里插入图片描述

Floyd(弗洛伊德)算法:

适用范围:

可求多源最短路
无负权回路即可(负权回路:a点到a点的权值和为负),边权可正可负,运行一次算法即可求得任意两点间最短路,可以判(负)环。
运用领接矩阵,求解数据量较小问题
dis作为邻接矩阵

dis[i][j] k  表示:i结点到j结点只经过前k号点的最短路程
for(int k=1;k<=n;k++)
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j])

其他应用:

传递闭包:

对于一个结点k,如果j能到k,i能到k,那么i就能到j,求传递闭包,就是把图中所有满足这样传递性的结点计算出来。

O(n^3)
for(int k=1;k<=n;k++)
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			if(dis[i][k]&&dis[j][k])
				dis[i][j]=1;
传递闭包重要优化bitset O(n^3/64)

时间复杂度:
用到较少

求最小环

无向图核心算法:
int map[][]=new int[n+1][n+1];//邻接矩阵
int dis[][]=new int[n+1][n+1];//dis[i][j]为i到j的最短路


for(int k=1;k<=n;k++)//k为中间结点 从1到n
{	
//第一部分:枚举经过i,j,k的最小环,此时i和j都要小于k,避免路线重复,之后才能更新k点
	for(int i=1;i<k;i++)
	{
		for(int j=i+1;j<k;j++)
		{ 
			if(map[i][k]==Integer.MAX_VALUE||map[k][j]==Integer.MAX_VALUE) continue;
			minload=Math.min(minload,dis[i][j]+map[i][k]+map[k][j]);
		}
	}
				
//第二部分:更新k点,往后更新最短路
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			dis[i][j]=Math.min(dis[i][j],dis[i][k]+dis[k][j]);
		}
	}
}

有向图核心算法:

dp[i][j] k  表示:i结点到j结点只经过前k号点的最短路程
for(int k=1;k<=n;k++)
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j])
int minload=Integer.MAX_VALUE;
for(int i=1;i<=n;i++)
	minload=Math.min(minload,dis[i][i]);

HDU 1599
为无向图题目

package shortestpath;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
//import java.io.InputstreamReader;
//2021年3月23日下午1:35:52
//writer:apple
public class floydhdu1599 {
	static int n;
	static int m;
	public static void main(String[] args) throws IOException {
		// TODO Auto-generated method stub
		BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
		while(true)
		{
			String t[]=br.readLine().split(" ");
			n=Integer.parseInt(t[0]);m=Integer.parseInt(t[1]);
			int minload=Integer.MAX_VALUE;
			int map[][]=new int[n+1][n+1];//邻接矩阵
			int dis[][]=new int[n+1][n+1];//dis[i][j]为i到j的最短路
			for(int i=0;i<=n;i++)
			{
				for(int j=0;j<=n;j++)
				{
					map[i][j]=Integer.MAX_VALUE;
				}
			}
			for(int i=0;i<m;i++)
			{
				String tt[]=br.readLine().split(" ");
				int a=Integer.parseInt(tt[0]);int b=Integer.parseInt(tt[1]);int weignt=Integer.parseInt(tt[2]);
				map[a][b]=weignt;
				map[b][a]=weignt;
				dis[a][b]=weignt;
				dis[b][a]=weignt;
			}
			for(int k=1;k<=n;k++)//k为中间结点 从1到n
			{
				//第一部分:枚举经过i,j,k的最小环,此时i和j都要小于k,避免路线重复,之后才能更新k点
				for(int i=1;i<k;i++)
				{
					for(int j=i+1;j<k;j++)
					{ 
						if(map[i][k]==Integer.MAX_VALUE||map[k][j]==Integer.MAX_VALUE) continue;
						minload=Math.min(minload,dis[i][j]+map[i][k]+map[k][j]);
					}
				}
				
				//第二部分:更新k点,往后更新最短路
				for(int i=1;i<=n;i++)
				{
					for(int j=1;j<=n;j++)
					{
						dis[i][j]=Math.min(dis[i][j],dis[i][k]+dis[k][j]);
					}
				}
			}
			if(minload==Integer.MAX_VALUE) System.out.println("It's impossible.");
			else System.out.println(minload);
		}
	}
}

Dijkstra(迪杰斯特拉) 算法

适用范围:

只适用于不含负权边的图,本质为贪心。
为单源最短路径算法,只能求一个点到其他点的最短路径,并不像Floyd算法可以求任意两点的最短路。
时间复杂度为:
上限为O(N^2)
遍历n个点找最小值,为O(n),考虑用优先队列,直接取队首元素,就能降低为O(logn)

代码实现的:

邻接链表建图
优先队列
重载运算符
快读

经典:

public static void Dijkstra()
	{
		p.offer(new Edge(s,0));//起点 权值为0
		while(!p.isEmpty())
		{
			Edge t=p.poll();
			int from=t.to;
			int fromw=t.weight;
			if(vis[from]==1) continue;
			vis[from]=1;//只有从优先队列里弹出后才能确认是最短的,才能更新vis和dis
			for(int i=0;i<map[from].size();i++)
			{
				int next=map[from].get(i).to;
				int nextw=map[from].get(i).weight;
				if(dis[from]+nextw<dis[next])//起点经过from点到next的最小值 和 起点到next的最小值作比较
				{
					dis[next]=dis[from]+nextw;
//					if(vis[next]==0)
						p.offer(new Edge(next,nextw));
				}
			} 
		}
	}

优化时间后的:

public static void Dijkstra()
	{
		PriorityQueue<Edge> p=new PriorityQueue<Edge>((a,b)->a.weigth-b.weigth);
		p.add(new Edge(s,0));
		while(!p.isEmpty())
		{
			Edge t=p.poll();
			int from=t.to;
			int fromw=t.weigth;
			if(vis[from]) continue;
			vis[from]=true;
			dis[from]=fromw;
			for(Edge temp:map[from])
			{
				int next=temp.to;
				int nextw=temp.weigth;
				p.offer(new Edge(next,fromw+nextw));
			}			
		}		
	}

例题:

struct node{
	int to,w;
	node(int tt,int ww):to(tt),w(ww){}
	bool operator<(const node &cmp)const{
		return w>cmp.w;
	}
};
#include<bits/stdc++.h>
#include<iostream>

using namespace std;

const double eps = 1e-8; //const只是用来定义常量,常量在代码中一直存在,但define是预处理功能,有本质区别
const int maxx = 0x7f7f7f7f;//0x7f7f7f7f表示数据的无穷大
//常用的浮点数比较宏定义:
#define Equ(a,b) ((fabs((a)-(b)))<(eps)) //等于
#define More(a,b) (((a)-(b))>(esp)) //大于
#define Less(a,b) (((a)-(b))<(-esp))//小于
#define MoreEqu(a,b) (((a)-(b))>(-esp))//大于等于
#define LessEqu(a,b) (((a)-(b))<(esp))//小于等于
#define  MAX( x, y )  ( ((x) > (y)) ? (x) : (y) )//
//使用了algorithm头文件就可以直接使用max函数;
#define  MIN( x, y )  ( ((x) < (y)) ? (x) : (y) )
#define ll long long
#define PI 3.1415926
#define eps 1e-8
#define Conn(x,y) x##y;
int n;
int end;

struct node{
	int to,w;
	node(int tt,int ww):to(tt),w(ww){}
	bool operator<(const node &cmp)const{
		return w>cmp.w;
	}
};

vector<node> ma[2000];
bool vis[2000];
int dis[2000];

void dij(int x)
{
	priority_queue<node> p;
	p.push(node(x,0));
	while(!p.empty())
	{
		node t=p.top();p.pop();
		int from=t.to;
		int fromw=t.w;
		if(vis[from]) continue;
		vis[from]=true;
		dis[from]=fromw;
		for(int i=0;i<ma[from].size();i++)
		{
			int next=ma[from][i].to;
			int nextw=ma[from][i].w;
			p.push(node(next,fromw+nextw));
		} 
	}
}

int main()
{
	cin>>n>>end;
	for(int i=1;i<=n;i++)
	{
		int x,y,l;
		cin>>x>>y>>l;
		ma[x].push_back(node(y,l));
		ma[y].push_back(node(x,l));
	}
	dij(1);
	cout<<dis[end];
	return 0;
}

P4779 【模板】

未解决MLE和TLE的代码
package shortestpath;

//import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.util.LinkedList;
import java.util.PriorityQueue;


//2021年3月23日下午8:09:41
//writer:apple
public class DijkstraP4779 {

	static int n;
	static int m;
	static int s;
	static int dis[];//dis[i]为s到i的最短距离
	static int vis[];
	static class Edge{
		int to;
		int weight;
		public Edge(int t,int w) {
			to=t; weight=w;
		}
	}
	static LinkedList<Edge> map[];
	static PriorityQueue<Edge> p=new PriorityQueue<>((a,b)->a.weight-b.weight);
	public static void main(String[] args) throws IOException {
		// TODO Auto-generated method stub
//		BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
		StreamTokenizer st=new StreamTokenizer(new InputStreamReader(System.in));
//		String t[]=br.readLine().split(" ");
		st.nextToken();n=(int) st.nval;
		st.nextToken();m=(int)st.nval;
		st.nextToken();s=(int)st.nval;
		map=new LinkedList[n+1];
		dis=new int[n+1];
		vis=new int[n+1];
		for(int i=0;i<n+1;i++)
		{
			dis[i]=Integer.MAX_VALUE;
			map[i]=new LinkedList<Edge>();//讨论
		}
		dis[s]=0;
		for(int i=0;i<m;i++)
		{
//			int from=Integer.parseInt(tt[0]);int to=Integer.parseInt(tt[1]);int w=Integer.parseInt(tt[2]);
			st.nextToken();int from=(int)st.nval;
			st.nextToken();int to=(int)st.nval;
			st.nextToken();int w=(int)st.nval;
			map[from].add(new Edge(to,w));
		}
		Dijkstra();
		PrintWriter pt=new PrintWriter(new OutputStreamWriter(System.out));
		String ans="";
		for(int i=1;i<=n;i++)
		{
			ans+=dis[i]+" ";
		}
		pt.println(ans);
		pt.flush();
//		StringBuilder ans=new StringBuilder();
//		for(int i=1;i<=n;i++)
//			{
//				ans.append(dis[i]+" ");
//			}
//		System.out.println(ans);
	}
	public static void Dijkstra()
	{
		p.offer(new Edge(s,0));//起点 权值为0
		while(!p.isEmpty())
		{
			Edge t=p.poll();
			int from=t.to;
			int fromw=t.weight;
			if(vis[from]==1) continue;
			vis[from]=1;//只有从优先队列里弹出后才能确认是最短的,才能更新vis和dis
			for(int i=0;i<map[from].size();i++)
			{
				int next=map[from].get(i).to;
				int nextw=map[from].get(i).weight;
				if(dis[from]+nextw<dis[next])//起点经过from点到next的最小值 和 起点到next的最小值作比较
				{
					dis[next]=dis[from]+nextw;
//					if(vis[next]==0)
						p.offer(new Edge(next,nextw));
				}
			} 
		}
	}

}
经过多种优化后:

1,static全覆盖
2,优化输入:

StreamTokenizer st=new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));

3,优化输出:

PrintWriter pr=new PrintWriter(new OutputStreamWriter(System.out));
		for(int i=1;i<=n;i++)
		{
			ans.append(dis[i]);ans.append(" ");
		}
		pr.println(ans);
		pr.flush();

4,优化算法:

p.add(new Edge(s,0));
		while(!p.isEmpty())
		{
			Edge t=p.poll();
			int from=t.to;
			int fromw=t.weigth;
			if(vis[from]) continue;
			vis[from]=true;
			dis[from]=fromw;
			for(Edge temp:map[from])
			{
				int next=temp.to;
				int nextw=temp.weigth;
				p.offer(new Edge(next,fromw+nextw));
			}	
		}
AC代码:

package shortestpath;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.util.ArrayList;
import java.util.PriorityQueue;

//2021年3月24日上午9:50:14
//writer:apple
public class Dijkstra最终版 {

	static int n;
	static int m;
	static int s;
	static class Edge{
		int to;
		int weigth;
		public Edge(int t,int w)
		{
			to=t;
			weigth=w;
		}
	}
	static PriorityQueue<Edge> p=new PriorityQueue<Edge>((a,b)->a.weigth-b.weigth);
	static ArrayList<Edge> map[];
	static boolean vis[];
	static int dis[];
	
	public static void main(String[] args) throws IOException {
		// TODO Auto-generated method stub
		StreamTokenizer st=new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
		st.nextToken();n=(int)st.nval;
		st.nextToken();m=(int)st.nval;
		st.nextToken();s=(int)st.nval;
		
		//变量初始化
		map=new ArrayList[n+1];
		vis=new boolean[n+1];
		dis=new int[n+1];
		for(int i=0;i<n+1;i++)
		{
			map[i]=new ArrayList<Edge>();
			dis[i]=Integer.MAX_VALUE;
			vis[i]=false;
		}
		dis[1]=0;
		
		for(int i=0;i<m;i++)
		{
			st.nextToken();int f=(int)st.nval;
			st.nextToken();int t=(int)st.nval;
			st.nextToken();int w=(int)st.nval;
			map[f].add(new Edge(t,w));
		}

		Dijkstra();
		StringBuilder ans=new StringBuilder();
		PrintWriter pr=new PrintWriter(new OutputStreamWriter(System.out));
		for(int i=1;i<=n;i++)
		{
			ans.append(dis[i]);ans.append(" ");
		}
		pr.println(ans);
		pr.flush();
	}
	
	public static void Dijkstra()
	{
		
		p.add(new Edge(s,0));
		while(!p.isEmpty())
		{
			Edge t=p.poll();
			int from=t.to;
			int fromw=t.weigth;
			if(vis[from]) continue;
			vis[from]=true;
			dis[from]=fromw;
			for(Edge temp:map[from])
			{
				int next=temp.to;
				int nextw=temp.weigth;
				p.offer(new Edge(next,fromw+nextw));
			}
			
		}
		
	}
}

#include<bits/stdc++.h>
#include<iostream>

using namespace std;

const double eps = 1e-8; //const只是用来定义常量,常量在代码中一直存在,但define是预处理功能,有本质区别
const int maxx = 0x7f7f7f7f;//0x7f7f7f7f表示数据的无穷大
//常用的浮点数比较宏定义:
#define Equ(a,b) ((fabs((a)-(b)))<(eps)) //等于
#define More(a,b) (((a)-(b))>(esp)) //大于
#define Less(a,b) (((a)-(b))<(-esp))//小于
#define MoreEqu(a,b) (((a)-(b))>(-esp))//大于等于
#define LessEqu(a,b) (((a)-(b))<(esp))//小于等于
#define  MAX( x, y )  ( ((x) > (y)) ? (x) : (y) )//
//使用了algorithm头文件就可以直接使用max函数;
#define  MIN( x, y )  ( ((x) < (y)) ? (x) : (y) )
#define ll long long
#define PI 3.1415926
#define eps 1e-8
#define Conn(x,y) x##y;
int n,m;
int sta;
int dis[100005];
bool vis[100005];

struct node
{
	int to,w;
	node(int tt,int ww):to(tt),w(ww){
	}
	bool operator <(const node &cmp)const
	{
		return w>cmp.w;
	}
};

vector<node> mapp[100005];
void dijkstra(int x)
{
	priority_queue<node> q;
	q.push(node(x,0));
	while(!q.empty())
	{
		node now=q.top();q.pop();
		int from=now.to;
		int fromw=now.w;
		if(vis[from])continue;
		vis[from]=true;
		dis[from]=fromw;
		for(int i=0;i<mapp[from].size();i++)
		{
			int next=mapp[from][i].to;
			int nextw=mapp[from][i].w;
			q.push(node(next,fromw+nextw));
		}
	}
}
int main()
{
	cin>>n>>m>>sta;
	for(int i=1;i<=m;i++)
	{
		int x,y,l;
		cin>>x>>y>>l;
		mapp[x].push_back(node(y,l));
		mapp[y].push_back(node(x,l));
	}
	dijkstra(sta);
	cout<<dis[1];
	for(int i=2;i<=n;i++)
	{
		cout<<" "<<dis[i];
	}
	return 0;
}
L3-005 垃圾箱分布
#include<bits/stdc++.h>
#include<iostream>

using namespace std;

const double eps = 1e-8; //const只是用来定义常量,常量在代码中一直存在,但define是预处理功能,有本质区别
const int maxx = 0x7f7f7f7f;//0x7f7f7f7f表示数据的无穷大
//常用的浮点数比较宏定义:
#define Equ(a,b) ((fabs((a)-(b)))<(eps)) //等于
#define More(a,b) (((a)-(b))>(esp)) //大于
#define Less(a,b) (((a)-(b))<(-esp))//小于
#define MoreEqu(a,b) (((a)-(b))>(-esp))//大于等于
#define LessEqu(a,b) (((a)-(b))<(esp))//小于等于
#define  MAX( x, y )  ( ((x) > (y)) ? (x) : (y) )//
//使用了algorithm头文件就可以直接使用max函数;
#define  MIN( x, y )  ( ((x) < (y)) ? (x) : (y) )
#define ll long long
#define PI 3.1415926
#define eps 1e-8
#define Conn(x,y) x##y;
const int maxn=1e4+10;
int n,m,k,mmax;

struct node
{
	int v,w;
	node(int vv,int yy)
	{
		this->v=vv;
		this->w=yy;
	}
};

bool operator<(node a,node b)
{
	return a.w>b.w;//升序,弹出最短的权值 
}

vector<node> a[maxn];
int vis[maxn],d[maxn];


int get_num(string x)
{
	int l=x.size();
	int p=0,sum=0;
	if(x[0]=='G') p=1;
	for(int i=p;i<l;i++)
	{
		sum=sum*10+x[i]-'0';
	}
	if(p) return sum+n;
	return sum;
}

void dijkstra(int s)
{
	fill(vis,vis+maxn,0);
	priority_queue<node> q;
	fill(d+1,d+n+m+1,maxx);
	d[s]=0;
	q.push(node(s,0));
	while(!q.empty())
	{
		node now=q.top();q.pop();
		int from=now.v;
		int fromw=now.w;
		if(vis[from]) continue;
		vis[from]=1;
		d[from]=fromw;
		for(int i=0;i<a[from].size();i++)
		{
			int next=a[from][i].v;
			int nextw=a[from][i].w;
			q.push(node(next,fromw+nextw));
		}
	}
}

int main()
{
	int z,nx,ny;
	string x,y;
	cin>>n>>m>>k>>mmax;
	getchar();
	for(int i=0;i<k;i++)
	{
		cin>>x>>y>>z;
		nx=get_num(x);
		ny=get_num(y);
		a[nx].push_back(node(ny,z));
		a[ny].push_back(node(nx,z));
	}
	bool sign=false;
	int no=0,ans=0,mmin=0;
	for(int i=n+1;i<=n+m;i++)//遍历每一个桶 
	{
		int j=1,sum=0,mind=maxx;
		dijkstra(i);
		for(;j<=n;j++)
		{
			if(d[j]>mmax) break;
			sum+=d[j];
			if(d[j]<mind) mind=d[j];
		}
		if(j>n)//表示有ans
		{
			sign=true;
			if(mind>mmin)
			{
				ans=sum;
				mmin=mind;
				no=i;
			}
			else if(mind==mmin && ans>sum)
			{
				ans=sum;no=i;
			}
		} 
	}
	if(!sign) printf("No Solution\n");
    else printf("G%d\n%.1f %.1f\n",no - n,mmin * 1.0,ans * 1.0 / n);
	return 0;
}

SPFA算法

适用范围:

求解单源最短路径
可以处理正边权,负边权的最短路,可以处理负环,可以判环。
算法平均复杂度为O(KM),有很高效率。但是出题人往往会卡极端数据,所以对于普通最短路问题,一定要慎用spfa
它是一个万能的算法,它不仅可以求最短路,而且负权边,负环,判环问题都能解决。
一般在Dijkstra解决不了的时候,用SPFA,SPFA为Bellman的优化算法。
思想:运用动态逼近法,通过一个点中转来缩短距离。只有一个点在上一轮中被松弛成功时,这一轮从这个点连出的点才有可能被成功松弛。(松弛:发现更短的路径)

1,Bellman_ford算法里最后return-1的判断条件写的是dist[n]>0x3f3f3f3f/2;而spfa算法写的是dist[n]==0x3f3f3f3f;其原因在于Bellman_ford算法会遍历所有的边,因此不管是不是和源点连通的边它都会得到更新;但是SPFA算法不一样,它相当于采用了BFS,因此遍历到的结点都是与源点连通的,因此如果你要求的n和源点不连通,它不会得到更新,还是保持的0x3f3f3f3f。

,2,Bellman_ford算法可以存在负权回路,是因为其循环的次数是有限制的因此最终不会发生死循环;但是SPFA算法不可以,由于用了队列来存储,只要发生了更新就会不断的入队,因此假如有负权回路请你不要用SPFA否则会死循环。

3, 由于SPFA算法是由Bellman_ford算法优化而来,在最坏的情况下时间复杂度和它一样即时间复杂度为 O(nm)O(nm) ,假如题目时间允许可以直接用SPFA算法去解Dijkstra算法的题目。(好像SPFA有点小小万能的感觉?)

4, 求负环一般使用SPFA算法,方法是用一个cnt数组记录每个点到源点的边数,一个点被更新一次就+1,一旦有点的边数达到了n那就证明存在了负环。

Dijkstra和SPFA区别:
Dijkstra:每个结点一旦出队说明已经为最短路径,vis[i]=true,表示访问过了,需要优先队列,只能处理正边权
SPFA:每个结点可能多次入队,vis[i]=true表示在队列内,出了队列要标记false,只要普通队列,可以处理负边权

应用:

求单源最短路径

可判负环

n个点中,cnt记录到某个点最短路经过边的数量,
当cnt>=n说明有环,cnt<n无环
加入cnt[];

public static boolean spfahuan()
	{
		l.add(new Edge(s,0));
		vis[s]=true;//表示在队列里
		while(!l.isEmpty())
		{
			Edge t=l.poll();
			vis[t.to]=false;
			for(Edge next:map[t.to])
			{
				if(dis[t.to]+next.w<dis[next.to])
				{
					dis[next.to]=dis[t.to]+next.w;
					cnt[next.to]=cnt[t.to]+1;
					if(cnt[next.to]>=n) return true;
					if(vis[next.to]) continue;
					l.add(next);
					vis[next.to]=true;
				}
			}
		}
		return false;
	}

一般模板:求1到n的最短距离

#include<bits/stdc++.h>

using namespace std;
#define ll long long
#define INF 0x7f7f7f7f

const int N=1e5+5;
int h[N], w[N], e[N], ne[N], idx;

//void add(int a, int b, int c)//起点a   终点b    权值c 
//{
//    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
//}
void add(int a,int b,int c)
{
	e[idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx++;
}

int dis[200000];
bool vis[200000];
int n,m;
void spfa(int x)
{
	fill(dis,dis+200000,INF);
	dis[x]=0;
	queue<int> q;
	q.push(x);
	vis[x]=true;
	while(!q.empty())
	{
		int now=q.front();q.pop();
		vis[now]=false;
		for(int i=h[now];i!=-1;i=ne[i])
		{
			int next=e[i];
			if(dis[now]+w[i]<dis[next])
			{
				dis[next]=dis[now]+w[i];
				if(vis[next]) continue;
				vis[next]=true;
				q.push(next);
			}
		}
	}
}

int main()
{
	cin>>n>>m;
	fill(h,h+N,-1);
	for(int i=0;i<m;i++)
	{
		int x,y,z;
		cin>>x>>y>>z;
		add(x,y,z);
	}
	spfa(1);
	if(dis[n]==INF) cout<<"impossible";
	else cout<<dis[n];
	return 0;
}

例题:

P3371 【模板】单源最短路径(弱化版)

  package shortestpath;
//2021年3月25日下午3:34:10
//writer:apple
import java.io.*;
import java.util.ArrayList;
import java.util.LinkedList;
public class Spfa {

	static int n;
	static int m;
	static int s;
	static class Edge{
		int to;
		int w;
		public Edge(int ttt,int www)
		{
			to=ttt;w=www;
		}
	}
	static boolean vis[];
	static int dis[];
	static ArrayList<Edge> map[];
	static LinkedList<Edge> l=new LinkedList<>();
	public static void main(String[] args) throws IOException{
		// TODO Auto-generated method stub
		StreamTokenizer st=new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
		st.nextToken();n=(int)st.nval;
		st.nextToken();m=(int)st.nval;
		st.nextToken();s=(int)st.nval;
		//
		map=new ArrayList[n+1];
		vis=new boolean[n+1];
		dis=new int[n+1];
		for(int i=0;i<=n;i++)
		{
			map[i]=new ArrayList<>();
			dis[i]=Integer.MAX_VALUE;
			vis[i]=false;
		}
		dis[s]=0;
		for(int i=0;i<m;i++)
		{
			st.nextToken();int a=(int)st.nval;
			st.nextToken();int b=(int)st.nval;
			st.nextToken();int c=(int)st.nval;
			map[a].add(new Edge(b,c));
		}
		spfa();
		for(int i=1;i<=n;i++)
		{
			System.out.print(dis[i]+" ");
		}
	}
	public static void spfa()
	{
		l.add(new Edge(s,0));
		vis[s]=true;//表示在队列里
		while(!l.isEmpty())
		{
			Edge t=l.poll();
			vis[t.to]=false;
			for(Edge next:map[t.to])
			{
				if(dis[t.to]+next.w<dis[next.to])
				{
					dis[next.to]=dis[t.to]+next.w;
					if(vis[next.to]) continue;
					l.add(next);
					vis[next.to]=true;
				}
			}
		}
	}
}
#include<bits/stdc++.h>
#include<iostream>

using namespace std;

const double eps = 1e-8; //const只是用来定义常量,常量在代码中一直存在,但define是预处理功能,有本质区别
const int maxx = 0x7f7f7f7f;//0x7f7f7f7f表示数据的无穷大
//常用的浮点数比较宏定义:
#define Equ(a,b) ((fabs((a)-(b)))<(eps)) //等于
#define More(a,b) (((a)-(b))>(esp)) //大于
#define Less(a,b) (((a)-(b))<(-esp))//小于
#define MoreEqu(a,b) (((a)-(b))>(-esp))//大于等于
#define LessEqu(a,b) (((a)-(b))<(esp))//小于等于
#define  MAX( x, y )  ( ((x) > (y)) ? (x) : (y) )//
//使用了algorithm头文件就可以直接使用max函数;
#define  MIN( x, y )  ( ((x) < (y)) ? (x) : (y) )
#define ll long long
#define PI 3.1415926
#define eps 1e-8
#define Conn(x,y) x##y;
int n,m;
int sta;
int dis[100005];
bool vis[100005];


struct node
{
	int to,w;
	node(int tt,int ww):to(tt),w(ww){
	}
};

vector<node> mapp[100005];
void spfa(int x)
{
	fill(dis,dis+100005,maxx);
	dis[x]=0;
	queue<node> q;
	q.push(node(x,0));
	vis[x]=true;
	while(!q.empty())
	{
		node now=q.front();q.pop();
		vis[now.to]=true;
		for(int i=0;i<mapp[now.to].size();i++)
		{
			int next=mapp[now.to][i].to;
			int nextw=mapp[now.to][i].w;
			if(dis[now.to]+nextw<dis[next])
			{
				dis[next]=dis[now.to]+nextw;
				if(vis[next]) continue;
				vis[next]=true;
				q.push(node(next,nextw));
			}
		}
	}
}

int main()
{
	cin>>n>>m>>sta;
	for(int i=1;i<=m;i++)
	{
		int x,y,l;
		cin>>x>>y>>l;
		mapp[x].push_back(node(y,l));
		mapp[y].push_back(node(x,l));
	}
	spfa(sta);
	cout<<dis[1];
	for(int i=2;i<=n;i++)
	{
		cout<<" "<<dis[i];
	}
	return 0;
}

Bellman-Ford(贝尔曼福特)算法

适用范围:

单源最短路径
对于有负权的图,可以完美解决求最短路径问题。
使用有向图,无向图,边权可正可负
可以判负环

思想:O(NM)

一直对边进行松弛,直到结束
一共需要N-1次松弛
1,第一轮松弛,对每一条边进行松弛
2,第二轮,重复松弛,直到结束

操作代码

1,建立边Edge[]
2,n-1轮松弛
3,判断是否环

#include<bits/stdc++.h>

using namespace std;
#define ll long long
#define INF 0x7f7f7f7f

const int N=1e5+5;
int dis[N];
int backup[N]; 
int n,m,k;
struct edge{
	int a,b,w;
}e[N];

bool bellman(int x)
{
	fill(dis,dis+N,INF);
	dis[x]=0;
	for(int i=1;i<=k;i++)
	{
		memcpy(backup,dis,sizeof(dis));//在限制了一个路径中最多k条边 的时候采用备份数组,防止串联 
		for(int j=0;j<m;j++)
		{
			dis[e[j].b]=min(dis[e[j].b],backup[e[j].a]+e[j].w);
		}
	}
	if(dis[n]>INF/2) return false;
	return true;
}

int main()
{
	cin>>n>>m>>k;
	for(int i=0;i<m;i++)
	{
		int x,y,z;
		cin>>x>>y>>z;
		e[i].a=x;e[i].b=y;e[i].w=z;
	}
	if(bellman(1)) cout<<dis[n];
	else  cout<<"impossible";
	return 0;
}
package shortestpath;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;

//2021年3月25日下午6:01:28
//writer:apple
public class Bellman_ford {
	static int n;
	static int m;
	static int s;
	static int dis[];
	static class Edge{
		int f,t,w;
//		public Edge()
//		{
//			
//		}
		public Edge(int from,int to,int weight)
		{
			f=from;t=to;w=weight;
		}
	}
	static Edge edge[];
	public static void main(String[] args) throws IOException {
		// TODO Auto-generated method stub
		StreamTokenizer st=new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
		st.nextToken();n=(int)st.nval;
		st.nextToken();m=(int)st.nval;
		st.nextToken();s=(int)st.nval;
		dis=new int[n+1];
		edge=new Edge[m+1];
		for(int i=0;i<=n;i++) dis[i]=Integer.MAX_VALUE;
		dis[s]=0;
		for(int i=1;i<=m;i++)
		{
			st.nextToken();int from=(int)st.nval;
			st.nextToken();int to=(int)st.nval;
			st.nextToken();int weight=(int)st.nval;
			if(from==s) dis[to]=weight;
			edge[i]=new Edge(from,to,weight);
		}
		if(bellman_ford())
		{
			for(int i=1;i<=n;i++)
			{
				System.out.print(dis[i]+" ");
			}
		}
	}
	public static boolean bellman_ford()
	{
		for(int i=1;i<n;i++)//n-1轮 松弛
		{
			for(int j=1;j<=m;j++)//共m条边
			{
				Edge temp=edge[j];
				if(dis[temp.f]+temp.w<dis[temp.t])
				{
					dis[temp.t]=dis[temp.f]+temp.w;
				}
			}
		}
		boolean flag=true;
		for(int i=1;i<=m;i++)
		{
			Edge temp=edge[i];
			if(dis[temp.f]+temp.w<dis[temp.t])
			{
				flag=false;break;
			}
		}
		return flag;
	}
}

例题

1,洛谷P1744

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值