有一种“链式前向星”的做法,这里暂不介绍.
存在一个二维数组g,g[i][j] = w 表示点i 到点j 有一条权值为w 的边.
优点: 写起来方便快捷.
缺点: 可扩展性低,存在冗余的内存消耗,使得内存消耗大.
code:
(略)
对于点i,存在一个向量vec[u] ,vec[u] 中存储着多个形如 {v, w} 的pair结构,表示点u 存在一条到点v权值为w 的边.
优点: 可以使用STL中的vector,动态拓展内存,无冗余的内存消耗.
缺点: 几乎没有,是常用的做法.
code:
#include
using namespace std;
const int N = 10086 * 100;
vector<pair<int, int> > vec[N];
int main()
{
//这里以无向图作为例子,假设点u与点v间存在权值为w的边
int u, v, w;
cin >> u >> v >> w;
vec[u].push_back({v, w});
vec[v].push_back({u, w});
return 0;
}
先说结论,dijkstra只适用于无负边权的有向或无向图.
接下来的定理证明会详细阐述这些内容.
首先,我们有一个无负边权图g ,d[i] 表示起点s到点i的最短距离.
集合A中的数i表示d[i] 已经是最小值,不可再松弛.
显而易见,d[s] = 0 ,这个值是一个常数,所以这是最小值。此时,A = {s} .
再找到一条权值最小的连接s的边{u, v, w} ,其中u = s,w 在所有u = s 的边中最小.
我们知道,图g 是无负边权图, 而d[s] = 0 这个值最小,所以不存在一条边能使得d[v] < d[s] + w (只有无负边权图满足),
所以d[s] + w 的值对于d[v] 是最小的,不可再松弛,此时,A = {s,v} .
同理,我们便可以使用这个d[v]再去更新其他点.
我们使用1.2中的邻接表来存图.
d[i] 表示起点s到点i的距离,这个值应该是不断变小的,所以我们把他的初值设为INF .
vis[i] 为true时表示点i 不可再松弛,我们将vis的初值设为false.
然后我们就知道,d[s] = 0 .
接着,不断如此循环:从起点开始找一个d[i] 值最小的点,满足vis[i] = false ,将vis[i] = true .
然后遍历他的所有出边,找到权值最小的{u, v, w} ,若d[v] > d[u] + w ,便更新d[v] = d[u] + w .
只要所有点i 的vis[i] = true ,就会结束循环,时间复杂度应该是:
O ( N 2 ) . O(N^2) . O(N2).
code:(使用1.2邻接表存图)
(较容易,略)
2.3.1中算法的复杂度不够理想,运用在复杂的题目中有可能会TLE .
通过分析2.3.1中的算法,我们发现,找 “d[i] 值最小的点” 这个步骤可以进行优化.
可以使用优先队列,保证每次取出的队头元素的d[i] 都是最小.
每次取队头元素这个操作的时间复杂度是:
O ( log 2 N ) O(\log_2N) O(log2N)
所以优先队列优化后的时间复杂度应该是:
O ( M ⋅ log 2 N ) O(M \cdot \log_2N) O(M⋅log2N)
code:(以有向无负边权图为例子)
#include
using namespace std;
const int N = 1008600;
vector<pair<long long, long long> > g[N];
long long d[N];
bool vis[N];
priority_queue<pair<long long, long long> > q;
int main()
{
memset(vis, false, sizeof vis);
long long n, m, s;
scanf("%lld %lld %lld", &n, &m, &s);//输入n、m、s,分别对应点数、边数、起点
for(long long i = 1; i <= n; i++) d[i] = 2147483647;
for(long long i = 0; i < m; i++) {
long long u, v, w;
scanf("%lld %lld %lld", &u, &v, &w);//有向图,这里u,v,w指点u到点v有一条权值为w的边
g[u].push_back({v, w});
}
d[s] = 0;
q.push({0, s});
while(!q.empty()) {
long long u = q.top().second;
q.pop();
if(vis[u]) continue;
vis[u] = true;
for(size_t i = 0; i < g[u].size(); i++) {
long long v = g[u][i].first,
w = g[u][i].second;
if(d[v] > d[u] + w) {
d[v] = d[u] + w;
q.push({-d[v], v}); //让优先队列从小到大
}
}
}
for(long long i = 1 ; i <= n; i++) printf("%lld ", d[i]);
return 0;
}
模板题:
洛谷P3371
洛谷P4779
非模板题:
洛谷P1144
JiansYuan
2020.8.3