Dijkstra算法
朴素版
双重循环时间复杂度:N^2
求单源最短路径:从起点出发,到其他所有点的最短路径
除了存储图之外,还需要存储两个量
int dist[n] //存储每个点到起点的最短距离
bool st[n] //更新最短距离(判断当前点的最短路是否确定,为确定则更新
找到一个最优的顶点t,使得它到每个顶点的路径都是最短的(贪心)
int t = -1; //Dijkstra算法适用于不存在负权边的图
for(int j = 1; j <= n; j++){
if( !st[j] &&( t = -1 || dist[t] > dist[j])
//找到剩余未确定最短路的点中,距离最短的点
t = j;
}
当前的最短路已经确定,将该点标记
st[t] = true;
用t去更新其余未确定点到起点的最短距离
for(int j = 1; j <= n; j++)
dist[j] = min(dist[j], dist[t] + g[t][j]);
/*从1开始不会影响之前已经确定的点的最小距离,因为先确定的点距离已经是之前的
最小距离,j从1开始能使代码更简洁*/
完整代码:
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 510;
int g[N][N]; //为稠密阵所以用邻接矩阵,存储边权值
int dist[N]; //用于记录每一个点距离顶点的距离
bool st[N]; //用于记录该点的最短距离是否已经确定
int n,m;
int Dijkstra()
{
memset(dist, 0x3f,sizeof dist); //初始化距离 0x3f代表无限大
dist[1] = 0; //第一个点到起点的距离为0
//迭代n次,每次可以确定一个点到起点的最短路
for(int i=0;i<n;i++)
{
int t = -1;
for(int j = 1;j <= n;j++)
//找到未在s中标记过且离起点最近的点(n^2)
if(!st[j]&&(t==-1||dist[t]>dist[j]))
t = j;
st[t]=true; //将该点标记(n)
for(int j=1;j<=n;j++)
//用t更新其他未确定最短路的点到起点的距离(n^2)
dist[j]=min(dist[j],dist[t]+g[t][j]);
}
if(dist[n]==0x3f3f3f3f) return -1; //如果第n个点不存在最短路径(1和n不连通
return dist[n]; //存在则返回最短距
}
int main()
{
cin>>n>>m;
memset(g,0x3f,sizeof g); //初始化图 因为是求最短路径,所以每个点初始为无限大
while(m--) //根据边数录入相邻的一组顶点以及它们之间的边
{
int x,y,z;
cin>>x>>y>>z;
g[x][y]=min(g[x][y],z); //如果发生重边的情况则保留最短的一条边
}
cout<<Dijkstra()<<endl;
return 0;
}
堆优化
时间复杂度:NlogN
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];
}
#include <cstring>
#include <iostream>
using namespace std;
const int N = 510;
int n, m, S, T;
int w[N], d[N][N];
int dist[N], cnt[N], sum[N];
bool st[N];
void dijkstra()
{
memset(dist, 0x3f, sizeof dist);
dist[S] = 0, cnt[S] = 1, sum[S] = w[S];
for (int i = 0; i < n; i ++ )
{
int t = -1;
for (int j = 0; j < n; j ++ )
if (!st[j] && (t == -1 || dist[t] > dist[j]))
t = j;
st[t] = true;
for (int j = 0; j < n; j ++ )
if (dist[j] > dist[t] + d[t][j])
{
dist[j] = dist[t] + d[t][j];
cnt[j] = cnt[t];
sum[j] = sum[t] + w[j];
}
else if (dist[j] == dist[t] + d[t][j])
{
cnt[j] += cnt[t];
sum[j] = max(sum[j], sum[t] + w[j]);
}
}
}
int main()
{
cin >> n >> m >> S >> T;
for (int i = 0; i < n; i ++ ) cin >> w[i];
memset(d, 0x3f, sizeof d);
while (m -- )
{
int a, b, c;
cin >> a >> b >> c;
d[a][b] = d[b][a] = min(d[a][b], c);
}
dijkstra();
cout << cnt[T] << ' ' << sum[T] << endl;
return 0;
}