求最短路径的2种基本方法——Floyd算法与Dijkstra算法

最短路径问题

问题解释:
从图中的某个顶点出发到达另外一个顶点的所经过的边的权重和最小的一条路径,称为最短路径

关于SPFA算法

点击这里

Floyd算法

Floyd算法介绍

算法的特点:
弗洛伊德算法是解决任意两点间的最短路径的一种算法,可以正确处理有向图或无向图或负权(但不可存在负权回路)的最短路径问题,同时也被用于计算有向图的传递闭包。

浅谈floyd算法

floyd算法的精髓在于“中转点”,例如:有个人在A点想去C点,但A到C不能直达,假设这时候有个B点,B点可以直达C点
那么我们可以先经过B点在到C点
floyd算法既是如此,假设一个二维数组为 e [ 110 ] [ 110 ] , 其中一点 e [ i ] [ j ] 代表 i 点到 j 点的距离 e[110][110],其中一点e[i][j]代表i点到j点的距离 e[110][110],其中一点e[i][j]代表i点到j点的距离
如果 i 点不能直达 j 点,假设有一个中转点 k ,我们可以先经过 k ,然后在到 j ,那么状态转移为 [ i ] [ k ] + [ k ] [ j ] 如果i点不能直达j点,假设有一个中转点k,我们可以先经过k,然后在到j,那么状态转移为[i][k]+[k][j] 如果i点不能直达j点,假设有一个中转点k,我们可以先经过k,然后在到j,那么状态转移为[i][k]+[k][j]
接着让我们看看例题

经典例题

例题来源

点击这里

思路

Floyd模板题,注意:题目给定的是无向图,我们需要统计两条边(还要注意重边,重边取最小值)
首先需要初始化,将不能到的点全部设为无穷大
接着按上面讲的状态转移方程 e [ i ] [ k ] + e [ k ] [ j ] e[i][k]+e[k][j] e[i][k]+e[k][j]与自身 e [ i ] [ j ] e[i][j] e[i][j]比较大小,取最小值即可

code

#include<bits/stdc++.h>
#define int long long 
#define endl "\n"
#define fi first
#define se second
#define PII pair<int,int> 
#define lowbit(x) ((x)&(-x))
#define ULL unsigned long long
using namespace std;
const int INF=INT_MAX;
const int N=110;
int e[N][N];
int n,m;
void Init(){//初始化
	for(int i=1;i<=n;++i)
	   for(int j=1;j<=n;++j){
	   	if(i==j) e[i][j]=0;
	   	else e[i][j]=INF;
	   }
}
void floyd(){
	for(int k=1;k<=n;++k)
	   for(int i=1;i<=n;++i)
	   	  for(int j=1;j<=n;++j){
	   	  	if(e[i][j]>e[i][k]+e[k][j]){
	   	  	 	e[i][j]=e[i][k]+e[k][j];//取最小值
			  }
		   }
}
void solve(){	
	cin >> n >> m;
    Init();
	for(int i=1;i<=m;++i){
		int u,v,w;
		cin >> u >> v >> w;
		e[u][v]=e[v][u]=min(e[u][v],w);//重边取最小值
	}
	floyd();
	for(int i=1;i<=n;++i){
	   for(int j=1;j<=n;++j){
	   	cout << e[i][j] << " ";
	   }
	   cout << endl;
    }
	return ;
}
signed main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	int t=1;
	//cin >> t;
	while(t--) solve();
	// cout << fixed;//强制以小数形式显示
    // cout << setprecision(n); //保留n位小数
	return 0;
}

Dijkstra算法

Dijkstra算法介绍

算法特点:
迪杰斯特拉算法可以算出从一个顶点到其余各顶点的最短路径,解决的是有权图中最短路径问题

浅谈Dijkstra算法

迪杰斯特拉算法主要特点是从起始点开始,采用贪心算法的策略,每次遍历到始点距离最近且未访问过的顶点的邻接节点,直到扩展到终点为止。

经典例题

例题来源

点击这里

思路

Dijkstra算法的模板题,采用贪心的策略,每次遍历与起始点最近且未被访问,同时权值还是最小的点
找到这个点后,将这个点进行标记(说明起始点到该点的距离已经是最短了
然后从该点进行拓展延伸,比较当前拓展的值与拓展之前的权值的大小,取最小值
然后继续拓展,直到拓展到终点结束

code

#include<bits/stdc++.h>
#define int long long 
#define endl "\n"
#define fi first
#define se second
#define PII pair<int,int> 
#define lowbit(x) ((x)&(-x))
#define ULL unsigned long long
using namespace std;
const int INF=INT_MAX;
const int mod=1e9+7;
const int N=1e4+5;
int e[N][N],dis[N],vis[N];
int n,m;
int x,y;
int flag=0;
void Init(){
	for(int i=1;i<=n;++i)
	   for(int j=1;j<=n;++j){
	   	if(i==j) e[i][j]=0;
		else e[i][j]=INF; 
	   }
	for(int i=1;i<=n;++i){
		vis[i]=0;
	} 
}
void dijkstra(){
	for(int i=1;i<=n;++i) dis[i]=e[x][i];//从x点初始化
	vis[x]=true;
	for(int i=1;i<=n;++i){
    	int k=-1;
    	for(int j=1;j<=n;++j){
    		if(vis[j]==0 && (k==-1||dis[k]>dis[j]))//贪心策略,选最小值
    		   k=j;
		}
		if(dis[k]==INF){
			flag=1;
			return ;
		}
		if(k==-1) break;
		vis[k]=1;
		for(int j=1;j<=n;++j){
			if(vis[j]==0) dis[j]=min(dis[k]+e[k][j],dis[j]);//拓展延伸
		}
	}
}
void solve(){	
	cin >> n >> m;
	Init();
	for(int i=1;i<=m;++i){
		int a,b,c;
		cin >> a >> b >> c;
		e[a][b]=min(c,e[a][b]);//去重边
	} 
	cin >> x >> y;
	flag=0;//判断是否可以到达
	dijkstra();
	if(flag) cout << "NO" << endl;
	else cout << dis[y] << endl;
	return ;
}
signed main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	int t=1;
	cin >> t;
	while(t--) solve();
	// cout << fixed;//强制以小数形式显示
    // cout << setprecision(n); //保留n位小数
	return 0;
}
  • 22
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值