理论知识
dijkstra三部曲
朴素版dijkstra
模拟过程
堆优化版dijksra
经典模版例题
Dijkstra求最短路 I
参加科学大会(第六期模拟笔试)--模版题
网络延迟
ref
理论知识
最短路是图论中的经典问题即:给出一个有向图,一个起点,一个终点,问起点到终点的最短路径。
dijkstra算法:在有权图(权值非负数)中求从起点到其他节点的最短路径算法。需要注意两点:
- dijkstra 算法可以同时求 起点到所有节点的最短路径
- 权值不能为负数
dijkstra三部曲
- 第一步,选源点到哪个节点近且该节点未被访问过;(minDist数组里的数值,结合visited数组筛选出未访问的节点)
- 第二步,该最近节点被标记访问过;(更新visited数组)
- 第三步,更新非访问节点到源点的距离(即更新minDist数组)。minDist数组 用来记录 每一个节点距离源点的最小距离。(更新minDist数组)(初始化的时候就应该初始为最大值)
朴素版dijkstra
模拟过程
①初始化:节点0 不做处理,统一从下标1 开始计算,源点(节点1) 到自己的距离为0,所以 minDist[1] = 0; 此时所有节点都没有被访问过,所以 visited数组都为0。
②dijkstra 三部曲 :
更新 minDist数组,即:源点(节点1) 到 节点6 、 节点3 和 节点4【与当前被访问节点2相连的节点】的距离。
- 源点到节点6的最短距离为5,小于原minDist[6]的数值max,更新minDist[6] = 5
- 源点到节点3的最短距离为3,小于原minDist[3]的数值4,更新minDist[3] = 3
- 源点到节点4的最短距离为6,小于原minDist[4]的数值max,更新minDist[4] = 6
- 源点到节点4的最短距离为5,小于原minDist[4]的数值6,更新minDist[4] = 5
...........
...........最终
节点1)到终点(节点7)的最短距离就是 minDist[7] ,按上面举例讲解来说,minDist[7] = 12,节点1 到节点7的最短路径为 12。
堆优化版dijksra
1、邻接表结构存储: vector<vector<pair<int, int>>> adj(n + 1); // 邻接表直接存储边
2、初始化最 短距离数组和优先队列,设定起点距离
使用小顶堆(priority_queue
)存储 <距离, 节点>
对,初始时将起点 (0, 1)
加入队列
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> pq;
3、主循环:通过优先队列快速找到当前最短路径节点,并更新邻接节点。
用到的数据结构:
vector<vector<pair<int, int>>> adj(n + 1)
adj[1] = { {2, 2}, {3, 4} }; // 从 1 出发,有 1→2 (权2),1→3 (权4) adj[2] = { {3, 1}, {4, 7} }; // 从 2 出发,有 2→3 (权1),2→4 (权7)
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> pq;
动态维护当前扩展的最短路径节点
经典模版例题
Dijkstra求最短路 I
完整代码:
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,m;
cin>>n>>m;
vector<vector<int>> grid(n+1,vector<int>(n+1,INT_MAX));
for(int i=1;i<=m;i++)
{
int x,y,z;
cin>>x>>y>>z;
if (z < grid[x][y]) { // 处理重边,保留最小权重
grid[x][y] = z;
}
}
vector<int> minDist(n+1,INT_MAX);
vector<bool> visited(n+1,false);
int start=1;
minDist[start]=0;
for(int i=1;i<=n;i++)
{
int minval=INT_MAX;
int cur=-1;
for(int j=1;j<=n;j++)
{
if(!visited[j]&&minDist[j]<minval)
{
minval=minDist[j];
cur=j;
}
}
if (cur == -1) break; // 所有剩余节点不可达
visited[cur]=true;
for(int j=1;j<=n;j++)
{
if(!visited[j]&&grid[cur][j]!=INT_MAX&&grid[cur][j]+minDist[cur]<minDist[j])
{
minDist[j]=minDist[cur]+grid[cur][j];
}
}
}
if(minDist[n]==INT_MAX) cout<<"-1";
else cout<<minDist[n];
return 0;
}
注意重边?????!!!!
堆优化版:
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
vector<vector<pair<int, int>>> adj(n + 1); // 邻接表直接存储边
// 读取边并构建邻接表(无需预处理二维数组)
for (int i = 0; i < m; ++i) {
int x, y, z;
cin >> x >> y >> z;
adj[x].emplace_back(y, z); // 直接添加所有边
}
// Dijkstra算法
vector<int> dist(n + 1, INT_MAX);
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> pq;
dist[1] = 0;
pq.push({0, 1});
while (!pq.empty()) {
auto [d, u] = pq.top();
pq.pop();
if (d > dist[u]) continue; // 跳过过时记录
for (auto [v, w] : adj[u]) {
if (dist[v] > dist[u] + w) {
dist[v] = dist[u] + w;
pq.push({dist[v], v});
}
}
}
// 输出结果
cout << (dist[n] == INT_MAX ? -1 : dist[n]);
return 0;
}
参加科学大会(第六期模拟笔试)--模版题
完整代码: (我真的好爱卡哥,代码简洁又易懂!!!!!)
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,m;
cin>>n>>m;
//邻接矩阵存储
vector<vector<int>> grid(n+1,vector<int>(n+1,INT_MAX));//初始化数值为INT_MAX
for(int i=0;i<m;i++) //输入边权
{
int s,e,v;
cin>>s>>e>>v;
grid[s][e]=v;//存储边权值
}
int start=1;//记录源点,从哪个点开始走
int end=n;//记录终点
vector<int> minDist(n+1,INT_MAX);//存储每个点至源点的最小距离
minDist[start]=0;//源点到自身距离为0;
vector<bool> visited(n+1,false);//记录该点是否被访问过,初始化都没有被访问过
//依次遍历所有结点
for(int i=1;i<=n;i++)
{
int minval=INT_MAX;
int cur=start;//记录当前被访问的点中哪个点距离源点最近
//1、选距离源点最近&&没有被访问过的点
for(int v=1;v<=n;v++)
{
if(!visited[v]&&minDist[v]<minval)
{
minval=minDist[v];
cur=v;//记录该最近结点的id
}
}
//2、标记该最近结点beifnagw
visited[cur]=true;
//3、更新其他非访问节点到源点的距离(更新minDist数组)
for(int v=1;v<=n;v++)
{
//没有被访问过的、与 cur相连的、更新后minDist更小的
if(!visited[v]&&grid[cur][v]!=INT_MAX &&minDist[v]>minDist[cur]+grid[cur][v])
minDist[v]=minDist[cur]+grid[cur][v];
}
}
//不能到达终点
if(minDist[end]==INT_MAX) cout<<-1<<endl;
else cout<<minDist[end]<<endl; //输出到达终点的最短路径
return 0;
}
堆优化版:
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
// 邻接表存储:adj[x]保存从x出发的所有边,格式为<目标节点, 边权>
vector<vector<pair<int, int>>> adj(n + 1);
// 处理输入并保留最小边权(处理重边)
vector<vector<int>> grid(n + 1, vector<int>(n + 1, INT_MAX));
for (int i = 0; i < m; ++i) {
int s, e, v;
cin >> s >> e >> v;
if (v < grid[s][e]) grid[s][e] = v; // 仅保留最小权重
}
// 将邻接矩阵转换为邻接表
for (int x = 1; x <= n; ++x) {
for (int y = 1; y <= n; ++y) {
if (grid[x][y] != INT_MAX) {
adj[x].push_back({y, grid[x][y]});
}
}
}
// Dijkstra堆优化实现
vector<int> dist(n + 1, INT_MAX); // 存储起点到各节点的最短距离
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> pq; // 小顶堆,格式<距离, 节点>
dist[1] = 0; // 起点距离初始化为0
pq.push({0, 1}); // 将起点加入堆
while (!pq.empty()) {
auto [d, u] = pq.top(); // 取出当前距离最小的节点
pq.pop();
if (d > dist[u]) continue; // 跳过过时的路径记录
for (auto [v, w] : adj[u]) { // 遍历所有邻接节点
if (dist[v] > dist[u] + w) { // 发现更短路径
dist[v] = dist[u] + w; // 更新距离
pq.push({dist[v], v}); // 将新距离加入堆
}
}
}
// 输出结果:终点不可达则输出-1
cout << (dist[n] == INT_MAX ? -1 : dist[n]) << endl;
return 0;
}
网络延迟
#include<bits/stdc++.h>
using namespace std;
int main() {
int n, k, m;
cin >> n >> k >> m;
vector<vector<int>> grid(n+1, vector<int>(n+1, INT_MAX));
for(int i = 0; i < m; ++i) {
int u, v, w;
cin >> u >> v >> w;
grid[u][v] = w; // 存储有向边
}
vector<int> minDist(n+1, INT_MAX);
vector<bool> visited(n+1, false);
minDist[k] = 0; // 起点距离为0
for(int i = 1; i <= n; ++i) {
int minVal = INT_MAX;
int cur = -1;
// 寻找当前未访问的最小距离节点
for(int v = 1; v <= n; ++v) {
if (!visited[v] && minDist[v] < minVal) {
minVal = minDist[v];
cur = v;
}
}
// 所有剩余节点不可达
if (cur == -1) break;
visited[cur] = true;
// 更新相邻节点
for(int v = 1; v <= n; ++v) {
if (!visited[v] && grid[cur][v] != INT_MAX && minDist[cur] != INT_MAX) {
if (minDist[v] > minDist[cur] + grid[cur][v]) {
minDist[v] = minDist[cur] + grid[cur][v];
}
}
}
}
int maxTime = 0;
for(int i = 1; i <= n; ++i) {
if (minDist[i] == INT_MAX) {
cout << -1 << endl;
return 0;
}
maxTime = max(maxTime, minDist[i]);
}
cout << maxTime << endl;
return 0;
}
堆优化版:
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, k, m;
cin >> n >> k >> m;
vector<vector<pair<int, int>>> adj(n + 1); // 邻接表存储图
for (int i = 0; i < m; ++i) {
int u, v, w;
cin >> u >> v >> w;
adj[u].emplace_back(v, w);
}
// Dijkstra算法
vector<int> dist(n + 1, INT_MAX);
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> pq;
dist[k] = 0;
pq.push({0, k});
while (!pq.empty()) {
auto [d, u] = pq.top();
pq.pop();
if (d > dist[u]) continue;
for (auto [v, w] : adj[u]) {
if (dist[v] > dist[u] + w) {
dist[v] = dist[u] + w;
pq.push({dist[v], v});
}
}
}
int maxTime = *max_element(dist.begin() + 1, dist.end());
cout << (maxTime == INT_MAX ? -1 : maxTime) << endl;
return 0;
}
和模版几乎一样,只是最后判断为:所有的minDist不是INT_MAX(均可达),输出是最大的时间(所有举例源点的最短路径中最大的那一个!!!!!!) 好开心~
ref
代码随想录