Dijkstra解决单源最短路径
题目链接:
9.蓝桥王国 - 蓝桥云课https://www.lanqiao.cn/problems/1122/learning/?problem_list_id=30&page=1
题目描述:
小明是蓝桥王国的王子,今天是他登基之日。在即将成为国王之前,老国王给他出了道题,他想要考验小明是否有能力管理国家。
题目的内容如下:
蓝桥王国一共有 N 个建筑和 M 条单向道路,每条道路都连接着两个建筑,每个建筑都有自己编号,分别为 1∼N (其中皇宫的编号为 1)。国王想让小明回答从皇宫到每个建筑的最短路径是多少,但紧张的小明此时已经无法思考,请你编写程序帮助小明回答国王的考核。
输入描述:
输入第一行包含三个正整数 N,M。第 2 到 M+1 行每行包含三个正整数 u,v,w,表示 u→v之间存在一条距离为 w 的路。1≤N≤3×10^5,1≤m≤10^6,1≤ui,vi≤N,0≤wi≤10^9。
输出描述:
输出仅一行,共 N 个数,分别表示从皇宫到编号为 1∼N 建筑的最短距离,两两之间用空格隔开。(如果无法到达则输出 −1)
输入:
3 3
1 2 1
1 3 5
2 3 2
输出:
0 1 3
解题思路:
Dijkstra:先设置一个邻接二维数组(如果太大就设置为邻接表),邻接表定义如下:
typedef pair<int,long long> PLI; //定义节点类型
//创建邻接表
vector<vector<PLI>> adj(N + 1);
for (int i = 0;i < M;i++)
{
int u, v, w;
cin >> u >> v >> w;
adj[u].push_back({ v, w });
}
其创建了一个vector容器,每个vector容器包含一个类型变量为PLI的vector容器,外层容器定义为起点为i,内层容器(j,k)为终点为j,i和j的距离为k的节点,再定义一个小根堆队列,定义如下:
#include<queue>
//定义一个优先队列,最顶上为最小值
priority_queue<PLI, vector<PLI>, greater<PLI>> pq;
当选中距离源点最近的点v后,将点v的邻接边都进行判断,如果(源-v的距离+v-新节点的距离)<(源点-新节点的距离),则将新节点在dist上存储的距离更新,并将新的PLI加入到pq队列中,其代码如下:
while (!pq.empty())
{
int ed = pq.top().first; //终点
long long d = pq.top().second; //距离
pq.pop(); //退出dist中最小的节点
//更新ed节点的邻接节点
for(int i=0;i<adj[ed].size();i++)
{
if ((dist[ed] + adj[ed][i].second) < dist[adj[ed][i].first]) //起点到ed的距离+ed到v的距离<原本dist[v]的距离
{
dist[adj[ed][i].first] = dist[ed] + adj[ed][i].second; //更新dist
pq.push({ adj[ed][i].first ,dist[adj[ed][i].first] });
}
}
}
最终就可以得到完整的Dijkstra代码。
代码:
1、Dijkstra代码
//Dijkstra
void Dijkstra(vector<vector<PLI>> adj)
{
//初始化dist数组
dist[1] = 0;
//定义一个优先队列,最顶上为最小值
priority_queue<PLI, vector<PLI>, greater<PLI>> pq;
pq.push({ 1,0 }); //将起点距离设置为0
while (!pq.empty())
{
int ed = pq.top().first; //终点
long long d = pq.top().second; //距离
pq.pop(); //退出dist中最小的节点
//更新ed节点的邻接节点
for(int i=0;i<adj[ed].size();i++)
{
if ((dist[ed] + adj[ed][i].second) < dist[adj[ed][i].first]) //起点到ed的距离+ed到v的距离<原本dist[v]的距离
{
dist[adj[ed][i].first] = dist[ed] + adj[ed][i].second; //更新dist
pq.push({ adj[ed][i].first ,dist[adj[ed][i].first] });
}
}
}
}
2、完整代码
#include<iostream>
#include<vector>
#include<queue>
#include <climits>
#define MAX LLONG_MAX
using namespace std;
typedef pair<int,long long> PLI; //定义节点类型
vector<long long> dist(300010, MAX); //起点距离其余个点的距离
//Dijkstra
void Dijkstra(vector<vector<PLI>> adj)
{
//初始化dist数组
dist[1] = 0;
//定义一个优先队列,最顶上为最小值
priority_queue<PLI, vector<PLI>, greater<PLI>> pq;
pq.push({ 1,0 }); //将起点距离设置为0
while (!pq.empty())
{
int ed = pq.top().first; //终点
long long d = pq.top().second; //距离
pq.pop(); //退出dist中最小的节点
//更新ed节点的邻接节点
for(int i=0;i<adj[ed].size();i++)
{
if ((dist[ed] + adj[ed][i].second) < dist[adj[ed][i].first]) //起点到ed的距离+ed到v的距离<原本dist[v]的距离
{
dist[adj[ed][i].first] = dist[ed] + adj[ed][i].second; //更新dist
pq.push({ adj[ed][i].first ,dist[adj[ed][i].first] });
}
}
}
}
int main()
{
// 请在此输入您的代码
//输入
int N, M;
cin >> N >> M;
//创建邻接表
vector<vector<PLI>> adj(N + 1);
for (int i = 0;i < M;i++)
{
int u, v, w;
cin >> u >> v >> w;
adj[u].push_back({ v, w });
}
//更新dist数组
Dijkstra(adj);
//输出
for (int i = 1;i <= N;i++)
{
i != N ? (dist[i] != MAX ? cout << dist[i] << " " : cout << "-1" << " ") : (dist[i] != MAX ? cout << dist[i] << endl : cout << "-1" << endl);
}
return 0;
}
注意:
① 测试点2、4:在设置最大距离MAX的时候,为了防止溢出,需要将MAX设置为LLONG_MAX,其包含在头文件<climits>中;
②测试点3:需要将不可到达点输出为-1。