一、题意
输入一个n个点m条边的无向图,每条边给出边权,求从点1到点n的最短路径。
二、解析
最短路径模板题。用来复习一下Dijkstra算法的模板正好。
Dijkstra算法的时间复杂度为O(mlgn),一般对于边权为正的单源最短路问题就它就没错了。同时适用于有向图和无向图。
三、代码
#include <iostream> #include <queue> #include <vector> using namespace std; const int maxn = 1000 + 5, INF = 1 << 30; struct node { int u, d; node(int u, int d) : u(u), d(d) {} bool operator < (const node& rhs) const { return d > rhs.d; // 由于默认为最大堆, 而我们希望最小的先出列 } }; struct edge { int to, w; edge(int to, int w) : to(to), w(w) {} }; vector<edge> G[maxn]; int n, m, vis[maxn], d[maxn]; void Dij(int s) { fill(d, d + n + 1, INF), d[s] = 0; // 初始化d数组 fill(vis, vis + n + 1, 0); priority_queue<node> que; que.push(node(s, 0)); while(!que.empty()) { int u = que.top().u; que.pop(); if(vis[u]) continue; // vis判定 vis[u] = 1; for(int i = 0; i < G[u].size(); i ++) { const edge& e = G[u][i]; if(d[e.to] > d[u] + e.w) { // 松弛操作 d[e.to] = d[u] + e.w; que.push(node(e.to, d[e.to])); } } } } int main() { cin >> m >> n; while(m --) { int u, v, w; cin >> u >> v >> w; G[u].push_back(edge(v, w)); G[v].push_back(edge(u, w)); } Dij(1); cout << d[n] << endl; }
四、归纳
- Dijkstra算法用于求边权为正的单源最短路问题,模板如下:
struct node { // node结构是存放在优先队列中的结构 int u, d; node(int u, int d) : u(u), d(d) {} bool operator < (const node& rhs) const { return d > rhs.d; // 由于默认为最大堆, 而我们希望最小的先出列 } }; struct edge { //edge结构仅用于存图 int to, w; edge(int to, int w) : to(to), w(w) {} }; vector<edge> G[maxn]; int n, m, vis[maxn], d[maxn]; void Dij(int s) { fill(d, d + n + 1, INF), d[s] = 0; // 初始化d数组 fill(vis, vis + n + 1, 0); priority_queue<node> que; que.push(node(s, 0)); while(!que.empty()) { int u = que.top().u; que.pop(); if(vis[u]) continue; // vis就写在这两行,是为了忽略掉松弛前的结点(因为并没有从队列中删除,只是压入了d更小的状态而已), vis[u] = 1; // 因此放在这里进行判断,这是和bfs不同的 for(const edge& e : G[u]) { if(d[e.to] > d[u] + e.w) { // 松弛操作 d[e.to] = d[u] + e.w; que.push(node(e.to, d[e.to])); // 压入了松弛后的结点,然而并没有删除原结点,因此要依靠上面的vis } } } } int main() { ...... Dij(1); ...... }
注意Dijkstra的模板和bfs有点像,但是还是不一样的:
- 一个用优先队列,一个用普通队列
- 使用vis数组的原因不同