最短路之dijkstra算法
dijkstra比之上次介绍的的bellman-ford算法的用途上最大的区别就是dijkstra只可用于求无负权边图中的最短路,堆优化后的dij比bellman-ford的复杂度(mn)更小(mlogn)
代码源关于dijkstra的解释
简单来讲就是每次选出一个没被选过的离起点最近的点,松弛这个点所在的每个边,直到所有点都被选过。这样dist[n]就是起点到n的最短路
简单来讲就是每次选出一个没被选过的离起点最近的点,松弛这个点所在的每个边,直到所有点都被选过。这样dist[n]就是起点到n的最短路
对于每次选出一个没被选过的离起点最近的点我们可以用小根堆进行操作
例题1 dijkstra模板
给你一张 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
代码
# 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(这道题在bellman-ford的文章里写过,在这里再写一遍)
小蜗要在城市中租房子,他想要找到一处合适的住址,使得住址到商场、工作地点、医院的距离和最小。城市可以抽象为一张 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;
}