朴素版
模板
稠密图用邻接矩阵存储
int g[N][N]; // 存储每条边(**邻接矩阵存储)**
int dist[N]; // 存储1号点到每个点的最短距离
bool st[N]; // 存储每个点的最短路是否已经确定
// 求1号点到n号点的最短路,如果不存在则返回-1
int dijkstra()
{
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
for (int i = 0; i < n - 1; i ++ )
{
int t = -1; // 在还未确定最短路的点中,寻找距离最小的点
for (int j = 1; j <= n; j ++ )
if (!st[j] && (t == -1 || dist[t] > dist[j]))
t = j;
// 用t更新其他点的距离
for (int j = 1; j <= n; j ++ )
dist[j] = min(dist[j], dist[t] + g[t][j]);
st[t] = true;
}
if (dist[n] == 0x3f3f3f3f) return -1;
return dist[n];
}
优化
模板
稀疏图用邻接表存储
typedef pair<int, int> PII;
int n; // 点的数量
int h[N], w[N], e[N], ne[N], idx; // 邻接表存储所有边
int dist[N]; // 存储所有点到1号点的距离
bool st[N]; // 存储每个点的最短距离是否已确定
// 求1号点到n号点的最短距离,如果不存在,则返回-1
int dijkstra()
{
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
priority_queue<PII, vector<PII>, greater<PII>> heap;
heap.push({0, 1}); // first存储距离,second存储节点编号
while (heap.size())
{
auto t = heap.top();
heap.pop();
int ver = t.second, distance = t.first;
if (st[ver]) continue;
st[ver] = true;
for (int i = h[ver]; i != -1; i = ne[i])
{
int j = e[i];
if (dist[j] > distance + w[i])
{
dist[j] = distance + w[i];
heap.push({dist[j], j});//**注意入队位置**
}
}
}
if (dist[n] == 0x3f3f3f3f) return -1;
return dist[n];
}
问题
Dijkstra求最短路 I
代码
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=510;
int n,m;
int g[N][N];//**g[1][2]是指从1节点指向2节点的距离**,也可以表示不存在
int dist[N];//distance(距离)的缩写,代表每一个点到源点(起点)的距离
bool st[N];//state(状态)的缩写,当st[n]为true时说明这个点到源点的距离最小值就已经确定了
int dijkstra(){
memset(dist,0x3f,sizeof(dist));//存储每一个点到源点的距离//设置为一个无穷大的值,以便求最小值
dist[1]=0;//源点到自己的距离为0
for(int i=0;i<n-1;i++){//其实这条语句唯一的作用就是循环n-1次(优化了)
//所以写成for(int i=0;i<n;i++)也可以,因为如果下面的语句循环了n-1次的话,那么所有点都能得到最小值了
//可以这么理解,每次循环都会确定一个最小值,还会再创造一个最小值(留给下一次循环去确定)
//当循环n-1次时,情况是已经确定了n-1个点的最小值了,还创造了一个最小值(此时还有1个点等着下一次去确定)
//那么就不需要下一次循环了,毕竟剩下的就一个点,在1个点的集合中知道有一个点是最小值,顺理成章了
//当然你想写成for(int i=0;i<n;i++)也能AC~~小声说~~
int t=-1;//**找到离源点最近的未确定最小距离的点**
for(int j=1;j<=n;j++){//一个一个遍历寻找
if(!st[j] and (t==-1 or **dist[t]>dist[j]**)){//**出错点**
t=j;//!st[j]指的是最近距离还没有确定的点,and后面就是找符合!st[j]条件的距离最小的点
//这一个操作就是找到未确定最小值的 `点集`中的最小点,t==-1是当第一次遇到未确定~的点时能够被初始化
}
}
//(1)
for(int j=1;j<=n;j++){//现在找到t了,遍历一遍所有点,有一下几种情况
//1.j点和t点之间无连接,那么g[t][j]=0x3f3f3f3f,特别大,会被pass
//2.dist[j]<=dist[t]+g[t][j],源点到j点的距离,如果经过t后距离更长了,那么不考虑
//3.dist[j]<=dist[t]+g[t][j],,~~,经过t点距离更短了,那么修改dist[j]的值
dist[j]=min(dist[j],dist[t]+g[t][j]);
}
**st[t]=true**;//**当前t点已经把其余点全部遍历了一遍,此点变成确定距离为最小的点了**,这条语句放在(1)处也能AC//**出错点**
}
if(dist[n]==0x3f3f3f3f){//当前点n没被修改,说明到不了点n,输出-1
return -1;
}else{
return dist[n];//易证
}
}
int main(){
cin>>n>>m;//n存点数,m存边数
memset(g,0x3f,sizeof(g));//将点之间的距离的每一个值设置成很大的数,此知识点之前讲过
while(m--){
int a,b,c;
cin>>a>>b>>c;
g[a][b]=min(g[a][b],c);//有效解决多条边的问题,保留最短边//**题目中明确说可能有重边**
}
cout<<dijkstra()<<endl;
return 0;
}
Dijkstra求最短路 II
解决
我们依然沿用朴素dijkstra解法的思路,我们找到离源点最近的未被确定最小距离的点,让他来更新其他所有点
我们把找到最近的点的这个过程利用小根堆(优先队列)实现。
我们通过将找最小的这个过程使用堆来进行优化
错点
- 不能正确理解int h[N], e[N], ne[N], idx;的含义
代码
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
typedef pair<int, int> PII;
const int N = 1e6 + 10;
int n, m;
int h[N], e[N], ne[N], idx;//使用邻接表存储
int w[N];//存储边权
int dist[N];
bool st[N];
void add(int a, int b, int c)//如何处理重边和自环呢
//实际上并不需要刻意去处理,因为已经排好序了
//距离更近一定会优先排,然后st就被打上标记,会被continue掉
{
e[idx] = b, w[idx] = c/*这种存储方式是合理的*/, ne[idx] = h[a], h[a] = idx ++ ;
}
int dijkstra()
{
memset(dist, 0x3f, sizeof dist);//赋值
dist[1] = 0;
priority_queue<PII, vector<PII>, greater<PII>> heap;//优先队列就是堆
//这个优先队列是我们用来查找最小距离的辅助工具
// 数据类型, 容器, 排序方式
//greater 是小根堆排序
heap.push({0, 1});// 放入**距离, 节点编号**
while (heap.size()) //什么时候结束循环?
//从循环条件来看是当heap里面元素为空时结束循环
//那么什么时候heap为空呢?
//当所有点都已经访问完了,并且离原点更近的点一定会更优先进入循环
//因为小根堆排序,离原点近的放在前面就算不是与原点直接相连的点
//也会优先放进来
{
auto t = heap.top();//取出堆里面最小距离的点,然后释放掉
heap.pop();
int ver = t.second, distance = t.first;// ver节点编号
if (**st[ver]**) continue; //这时候已经取到了距离最近的点,如果已经
//访问过就结束此次循环,未访问过打上标记
//他是如何判断该点一定是他的距离最优点呢?
//转【1】
**st[ver] = true;//出错点,位置
当一个点的最短距离确定,就不需要更新,直接跳过**
for (int i = h[ver]; i != -1; i = ne[i]),
{
**int j = e[i];//j是节点编号**
if (dist[j] > dist[ver] + w[i])//【1】通过for循环遍历其指向的点
//当存在从起始点到j点更短的路径就进行更新
//为什么说他一定是最优距离呢?
//假设有其他点到ver的距离+其到原点的距离小于
//现有的距离,那么他一定会优先考虑,也就不会
//有该操作
{
dist[j] = dist[ver] + w[i];
heap.push({dist[j], j});// 为什么要将其放入堆中?
//应为存在从起始点到j点更短的路径
//同时也会对节点距离进行排序
//短距离放在前面
}
}
}
if (dist[n] == 0x3f3f3f3f) return -1;
return dist[n];
}
int main()
{
scanf("%d%d", &n, &m);
memset(h, -1, sizeof h);
while (m -- )
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
}
cout << dijkstra() << endl;
return 0;
}