最短路之dijkstra算法

本文介绍了Dijkstra算法的基本思想和与Bellman-Ford算法的区别,重点在于Dijkstra算法适用于无负权边图的最短路径问题。通过三个实例展示了Dijkstra算法在解决最短路径问题上的应用,包括单源最短路径和最小距离和问题,并提供了C++代码实现。
摘要由CSDN通过智能技术生成

   ijkstra比之上次介绍的的bellman-ford算法的用途上最大的区别就是dijkstra只可用于求无负权边图中的最短路,堆优化后的dij比bellman-ford的复杂度(mn)更小(mlogn)

代码源中关于dijkstra的算法介绍如下图

 

简单来讲就是每次选出一个没被选过的离起点最近的点,松弛这个点所在的每个边,直到所有点都被选过。这样dist[n]就是起点到n的最短路 

对于每次选出一个没被选过的离起点最近的点我们可以用小根堆或set进行操作

下面我们看几道例题来感受一下dijkstra是如何建图求解和优化的

例题1 : 

给你一张 n 个顶点 m 条边的有向简单图,顶点编号从 1 到 n,每条边都有一个边权,边权为非负整数。

现在有 k 组询问,每组询问读入两个整数 x,y请求出从 x 号点到 y 号点的最短路的长度。如果不存在从 x 号点到 y 号点的路径,请输出 -1

输入格式

第一行三个整数 n,m,k,表示图的顶点数、边数和询问次数。

接下来 m 行,每行三个整数 x,y,z,表示 x 号点到 y 号点有一条边权为 z 的有向边。

接下来 k 行,每行两个整数 x,y,表示一组询问。

输出格式

输出共 k 行,每行一个数表示一组询问的答案。

样例输入

3 3 2
1 2 3
2 3 2
3 2 1
1 3
3 1

样例输出

5
-1

数据规模

对于所有数据,保证 2≤n≤100000,0≤m≤200000,1≤k≤5,1≤x,y≤n,1≤z≤10000

 一道dijkstra裸题,主要给大家看一下模板

# include<bits/stdc++.h>

using namespace std;

typedef pair<int,int> pii;

const int N = 1e5+10,M = 2e5+10;

vector<pii> edge[N];	//edge数组存的是pair类型 {终点,权值} edge[x]的值为{y,w} 意味着一条起点为x终点为y权值为w的边
int dist[N];			//每个点到起点的距离

int n,m,k;
int dijkstra(int x,int y)
{
	priority_queue<pii,vector<pii>,greater<pii> > q;	//这个小根堆存的是{dist[i],i}
	memset(dist,0x3f,sizeof dist);
	dist[x] = 0;
	q.push({dist[x],x});	//最开始先把起点dist值设为0,传入堆
	while(q.size())
	{
		auto t = q.top();
		q.pop();
		int x = t.first;
		for(auto i:edge[t.second]) 	//类似于bfs的写法,将最近的点进队然后松弛临边
		{
			 if(dist[i.first] > x + i.second)
			 {
			 	dist[i.first] = x + i.second;
				q.push({dist[i.first],i.first});
			 }
		}
	}
	return dist[y];
	}

int main()
{
	cin>>n>>m>>k;
	while(m--)
	{
		int x,y,z;scanf("%d%d%d",&x,&y,&z);	
		edge[x].push_back({y,z});
	}
	while(k--)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		int ans = dijkstra(x,y);
		if(ans == 0x3f3f3f3f) cout<<"-1"<<endl;
		else cout<<ans<<endl;
	}
	
	return 0;
}

 

例题2

小蜗要在城市中租房子,他想要找到一处合适的住址,使得住址到商场、工作地点、医院的距离和最小。城市可以抽象为一张 n 个点 m 条边的无向简单图,顶点编号从 1 到 n,每条边都有一个边权,边权为非负整数。

输入格式

第一行两个整数 n,m,表示图的顶点数和边数。

接下来 m 行,每行三个整数 x,y,z,表示 x 号点和 y 号点之间有一条边权为 z 的边。

接下来一行三个整数 a,b,c,分别表示商场、工作地点、医院所在的顶点的编号。

输出格式

你需要寻找一个合适的住址(住址必须为 n 个顶点中的某个点,这个点可以和 a,b,c 重合),并输出一行一个整数表示住址到商场、工作地点、医院的最小距离和。

样例输入
4 3
1 2 1
2 3 1
3 4 1
1 2 4
样例输出
3
数据规模

对于所有数据,保证 3≤n≤5000,0≤m≤10000,1≤x,y,a,b,c≤n,1≤z≤10000保证整张图连通并且 a,b,c 两两不同。

这道题之前在bellmanford那篇文章中写过一遍,这次再用dijkstra写一遍,思路还是一样的:分别以a,b,c为起点做三次dijkstra建立三个dist数组然后遍历每个点求dist1+dist2+dist3的最小值

# include<bits/stdc++.h>

using namespace std;

typedef pair<int,int> pii;

const int M = 1e4+10;
const int N = 5e3+10;

vector<pii> edge[2*M+1];
int dist[N],dist1[N],dist2[N],dist3[N];
int a,b,c;

void dijkstra(int x)
{
	memset(dist,0x3f,sizeof dist);
	dist[x] = 0;
	priority_queue<pii,vector<pii>,greater<pii> > q;
	q.push({dist[x],x});
	while(q.size())
	{
		auto t = q.top();
		q.pop();
		for(auto i:edge[t.second])
		{
			if(dist[i.first]>t.first+i.second)
			{
				dist[i.first] = t.first+i.second;
				q.push({dist[i.first],i.first});
			}
		}
	}
	
}
int main()
{
	int n,m;cin>>n>>m;
	for(int i=0;i<m;i++) 
	{
		int x,y,z;scanf("%d%d%d",&x,&y,&z);
		edge[x].push_back({y,z});	
		edge[y].push_back({x,z});	
	}	
	scanf("%d%d%d",&a,&b,&c);
	dijkstra(a);memcpy(dist1,dist,sizeof dist);
	dijkstra(b);memcpy(dist2,dist,sizeof dist);
	dijkstra(c);memcpy(dist3,dist,sizeof dist);
	int ans = 100000;
	for(int i=1;i<=n;i++)
	{
		ans = min(ans,dist1[i]+dist2[i]+dist3[i]);
	}
	cout<<ans<<endl;
	return 0;
}

例题3

小蜗要在城市中租房子,他想要找到一处合适的住址,使得住址到商场、工作地点、医院的距离和最小。城市可以抽象为一张 n 个点 m 条边的无向简单图,顶点编号从 1 到 n,每条边都有一个边权,边权为非负整数。

输入格式

第一行两个整数 n,m,表示图的顶点数和边数。

接下来 m𝑚 行,每行三个整数 x,y,z,表示 x 号点和 y 号点之间有一条边权为 z 的边。

接下来一行三个整数 a,b,c,分别表示商场、工作地点、医院所在的顶点的编号。

输出格式

你需要寻找一个合适的住址(住址必须为 n 个顶点中的某个点,这个点可以和 a,b,c 重合),并输出一行一个整数表示住址到商场、工作地点、医院的最小距离和。

样例输入
4 3
1 2 1
2 3 1
3 4 1
1 2 4
样例输出
3
数据规模

对于所有数据,保证 3≤n≤5000,0≤m≤10000,1≤x,y,a,b,c≤n,1≤z≤10000保证整张图连通并且 a,b,c两两不同

思路:

本题主要难点就是这是一个有向图 从a到b的距离和从b到a的距离不同,所以对于每个点来回的时间我们都要考虑两部分 : 到k点需要的时间和从k点返回需要的时间

不妨设从k点返回的时间用数组dist1表示,到k点需要的时间用dist2表示。

dist1很好算,建图后以k为原点做一遍dijkstra就行

对于dist2我们不妨这样考虑 : 我们将所有边反向后建图,也就是a到k的时间为w反向后就是k到a为w,这样子走的还是a到k需要走的那条边但起点就又变成了k,然后再将所有边反向的图中以k为起点做一遍dijkstra

遍历每个顶点,dist1[i] + dist2[i]的最大值即为所求

# include<bits/stdc++.h>
# define int long long
using namespace std;

typedef pair<int,int> pii;

const int N = 1e5+10;
const int M = 1e5+10;

vector<pii> edge1[N];
vector<pii> edge2[N];
int dist1[N],dist2[N];

void dijkstra1(int x)
{
	memset(dist1,0x3f,sizeof dist1);
	dist1[x] = 0;
	priority_queue<pii,vector<pii>,greater<pii> > q;
	q.push({dist1[x],x});
	while(q.size())
	{
		auto t = q.top();
		q.pop();
		for(auto i:edge1[t.second])
		{
			if(dist1[i.first]>t.first+i.second)
			{
				dist1[i.first] = t.first+i.second;
				q.push({dist1[i.first],i.first});
			}
		}
	}
	
}

void dijkstra2(int x)
{
	memset(dist2,0x3f,sizeof dist2);
	dist2[x] = 0;
	priority_queue<pii,vector<pii>,greater<pii> > q;
	q.push({dist2[x],x});
	while(q.size())
	{
		auto t = q.top();
		q.pop();
		for(auto i:edge2[t.second])
		{
			if(dist2[i.first]>t.first+i.second)
			{
				dist2[i.first] = t.first+i.second;
				q.push({dist2[i.first],i.first});
			}
		}
	}
	
}

signed main()
{
	int n,m,k;cin>>n>>m>>k;
	for(int i=0;i<m;i++)
	{
		int x,y,z;scanf("%lld%lld%lld",&x,&y,&z);
		edge1[x].push_back({y,z});
		edge2[y].push_back({x,z});
	}
	dijkstra1(k);dijkstra2(k);
	int ans = 0;
	for(int i=1;i<=n;i++) ans = max(ans,dist1[i]+dist2[i]);
	cout<<ans<<endl;
	return 0;
}

 

  

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

拾墨乄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值