最短路——dijkstra与Floyd

图当中的最短路算法,主要分为单源最短路径与多源最短路径,本文分别讲解一种经典算法

单源最短路径——dijkstra

算法思路

单源最短路径意为从某个点到其余点的最短路径(权值最小)

算法本质为贪心思想,考虑局部最优。每次找到路径最短的节点标记走过

在每一次取最小节点时用路过该节点的路径更新存着最短路径的数组。

基础步骤

1、邻接表或链式前向星存图

2、重点:定义数组d[i],表示定点s到点i的最短路径

3、进行n-1次查询,每次找到d[x]最小值的x进行标记,遍历x的子节点,更新d数组(经过点x)

时间复杂度分析:n-1次查询,每次找最小值,时间复杂度   $O(n^2)$

但这样还不够!

不够快!

显然找最大值可以用堆优化

于是nlogn的代码就应运而生啦

小tips:取相反数存入实现小根堆哦

代码

#include<bits/stdc++.h>

using namespace std;
int n,m,s,d[314514];
int head[114514],idx,vis[214514];
struct edge{
	int v,next,w;
}edg[214514];
void add(int u,int v,int w){
	edg[++idx].w=w;
	edg[idx].v=v;
	edg[idx].next=head[u];
	head[u]=idx;
}//链式前向星存图
priority_queue< pair<int,int> > q;//堆
void dj(){
	memset(d,0x3f,sizeof(d));//方便取min
	d[s]=0;//初始点标为0
	q.push(make_pair(0,s));//初始化
	while(q.size()){
		int fa=q.top().second;//找到d数组最小值
		q.pop();//弹出
		if(vis[fa]) continue;//判断是否到过
		vis[fa]=1;//标记
		for(int i=head[fa];i;i=edg[i].next){//遍历子节点
			int y=edg[i].v,wy=edg[i].w;
			if(d[y]>d[fa]+wy) {//更新d数组,从节点fa到y的新路径
				d[y]=d[fa]+wy;
				q.push(make_pair(-d[y],y));//存入堆,注意取相反数实现小根堆
			}
		}
	}
}
int main()
{
    cin>>n>>m>>s;
	for(int i=1;i<=m;i++){
		int a1,a2,a3;
		cin>>a1>>a2>>a3;
		add(a1,a2,a3);
	}
	dj();
	for(int i=1;i<=n;i++) cout<<d[i]<<" ";

    return 0;
}

多源最短路径——Floyd

算法思路

意为从任意点到任意点的最短路径

算法基于简单动态规划,定义f[k][i][j]表示考虑前k个节点,从i到j的最短路径

与背包问题类似,在从小到大的循环中,k可以舍去,故f数组空间优化成了二维

为了满足dp从部分推广至全局的思路,借鉴区间dp,枚举i到j的中间点k,就易得状态转移方程

$f[i][j]=min(f[i][j],f[i][k]+f[k][j])$

时间复杂度 n^3

算法简单,贴贴代码吧

代码

#include<bits/stdc++.h>
using namespace std;
int n,a[114][4600],m,u,v,w;
int main(){
	cin>>n>>m;
	memset(a,0x3f,sizeof(a));
	for(int i=1;i<=m;i++){
		cin>>u>>v>>w;
		a[u][v]=min(a[u][v],w);
	}
	for(int k=1;k<=n;k++){
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				if((a[i][k]+a[k][j]<a[i][j])){
					a[i][j]=a[i][k]+a[k][j];
				}
			}
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			cout<<a[i][j]<<" ";
		}
		cout<<endl;
	}
	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值