2 1 1 2 3 3 3 1 2 5 2 3 5 3 1 2 0 0
3 2
Dijstra堆优化算法
#include <cstdio> #include <cstring> #include <iostream> #include <cstdlib> #include <queue> #include <vector> #include <limits> using namespace std; const int maxn = 150; const int INF = numeric_limits<int>::max(); struct Edge { int from, to, dist; Edge(int from, int to, int dist) : from(from), to(to), dist(dist) {} }; struct HeapNode { int d, u; HeapNode(int d, int u) : d(d), u(u) {} bool operator < (const HeapNode& hns) const { return d > hns.d; } }; struct Dijstra { int d[maxn], p[maxn], n; bool v[maxn]; vector<Edge> edges; vector<int> G[maxn]; void Init(int n) { this->n = n; edges.clear(); for (int i = 0; i < n; i++) G[i].clear(); for (int i = 0; i < n; i++) d[i] = INF; memset(v, false, sizeof(v)); } void AddEdges(int from, int to, int dist) { int m; edges.push_back(Edge(from, to, dist)); //将edges的位置哈希到G中 m = edges.size(); G[from].push_back(m - 1); edges.push_back(Edge(to, from, dist)); m = edges.size(); G[to].push_back(m - 1); } void dijstra(int s) { d[s] = 0; priority_queue<HeapNode> Q; Q.push(HeapNode(0, s)); while (!Q.empty()) { HeapNode hn = Q.top(); Q.pop(); int u = hn.u; if (v[u]) continue; v[u] = true; for (int i = 0; i < G[u].size(); i++) { Edge& e = edges[G[u][i]]; if (d[e.to] > d[e.from] + e.dist) { d[e.to] = d[e.from] + e.dist; p[e.to] = G[u][i]; //push() Q.push(HeapNode(d[e.to], e.to)); } } } } }; int main() { int N, M; Dijstra dij; while (~scanf("%d%d", &N, &M) && (N || M)) { dij.Init(N); int from, to, dist; for (int i = 0; i < M; i++) { scanf("%d%d%d", &from, &to, &dist); dij.AddEdges(from - 1, to - 1, dist); } dij.dijstra(0); printf("%d\n", dij.d[N - 1]); } return 0; }
Bellman-Ford算法
如果最短路存在,一定存在一个不含环的最短路
理由如下:在边权可正课负的图中,环游零环、正环和负环三种。如果包含零环或者正环,去掉以后路径不会边长;
如果包含负环,意味着最短路不存在,因为在负环中每绕一次,都会使路径长度减小,没有最小值
既然不含环,最短路径最多只经过(起点不算)n - 1个顶点,可以通过n - 1“轮”松弛来得到
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int maxn = 10005 * 2; //无向图,存两条边 const int INF = numeric_limits<int>::max(); int u[maxn], v[maxn], w[maxn], d[105]; int N, M; int main() { while (~scanf("%d%d", &N, &M) && (N || M)) { for (int i = 1; i <= N; i++) d[i] = INF; for (int i = 1; i <= M; i++) { scanf("%d%d%d", &u[i], &v[i], &w[i]); } for (int i = M + 1; i <= M + M; i++) { u[i] = v[i - M]; v[i] = u[i - M]; w[i] = w[i - M]; } d[1] = 0; for (int i = 0; i < N - 1; i++) { for (int k = 1; k <= M + M; k++) { int x = u[k], y = v[k]; if (d[x] < INF) { d[y] = min(d[y], d[x] + w[k]); //松弛 } } } printf("%d\n", d[N]); } return 0; }
用队列优化Bellman-Ford算法,用邻接表存图,减少对边的重复检查
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <queue> #include <vector> using namespace std; const int maxn = 10005 * 2; const int INF = numeric_limits<int>::max(); struct Edge { int u, v, w; Edge(int u, int v, int w) : u(u), v(v), w(w) {}; Edge() {}; }; int d[105]; int N, M; bool inq[105]; //标记某个点是否在queue里 queue<int> Q; vector<int> G[105]; vector<Edge> edges; int main() { while (~scanf("%d%d", &N, &M) && (N || M)) { //init for (int i = 1; i <= N; i++) d[i] = INF; memset(inq, false, sizeof(inq)); edges.clear(); for (int i = 1; i <= N; i++) G[i].clear(); while (!Q.empty()) Q.pop(); for (int i = 1; i <= M; i++) { int u, v, w; scanf("%d%d%d", &u, &v, &w); edges.push_back(Edge(u, v, w)); G[u].push_back(edges.size() - 1); edges.push_back(Edge(v, u, w)); //无向图,反向再输入一遍 G[v].push_back(edges.size() - 1); } d[1] = 0; inq[1] = true; Q.push(1); //bellman-ford队列优化,这个题不用判断负环了 while (!Q.empty()) { int p = Q.front(); Q.pop(); inq[p] = false; //用邻接表每次只找查与这个点连接的边,而上面一个程序每次要检查所有的边 for (int i = 0; i < G[p].size(); i++) { Edge& e = edges[G[p][i]]; if (d[p] < INF && d[p] + e.w < d[e.v]) { d[e.v] = d[p] + e.w; if (!inq[e.v]) { Q.push(e.v); inq[e.v] = true; } } } } printf("%d\n", d[N]); } return 0; }