- 单源最短路算法总结 by liuchuo大佬
- 单源最短路 from北大-算法设计与分析此视频的下一p是Dijkstra算法正确性的证明
Dijkstra算法的策略
- 全部结点V
- 源点src
- 集合S:目前已达结点集合,即已经确定最短路的结点的集合 (实现时,用visited数组维护),将结点x加入集合S即将visited【x】置为true。
- distance【i】:从src出发,经由集合S内的点,到结点i的最短路径长度
- 初始化
将src加入到S,distance【src】= 0;其他结点的distance为INF - 循环(n次,每次把一个点加入集合S)
2.1 找到 V-S 中距离src距离最短的点x,并将点x加入集合S
2.2 以点x为中介,更新集合S中点的distance(若有更短的路径,覆盖原有的)
正确性证明
1030 Travel Plan (30 分)
友好的最短路模板题
#include
#include
#include
#define INF 0x3fffffff
using namespace std;
int nn, mm, from, to, pre[501];
struct Highway {
int dis = INF, cost = INF;
};
int dist[501], cost[501] = {0};
Highway graph[501][501];
void Dijkstra(int src) {
bool visited[501] = {false};
fill(dist, dist + 501, INF);
fill(cost, cost + 501, INF);
dist[src] = 0, cost[src] = 0;
for (int i = 0; i < nn; ++i) {
int min_dist = INF, curr = -1;
for (int j = 0; j < nn; ++j) {
if (!visited[j] && dist[j] < min_dist) {
min_dist = dist[j];
curr = j;
}
}
if (curr != -1) {
visited[curr] = true;
for (int j = 0; j < nn; ++j) {
if (!visited[j] && graph[curr][j].dis != INF) {
int new_dist = dist[curr] + graph[curr][j].dis;
if (new_dist < dist[j]) {
dist[j] = new_dist;
cost[j] = cost[curr] + graph[curr][j].cost;
pre[j] = curr;
} else if (new_dist == dist[j] && cost[j] > cost[curr] + graph[curr][j].cost) {
cost[j] = cost[curr] + graph[curr][j].cost;
pre[j] = curr;
}
}
}
} else return;
}
}
int main() {
scanf("%d%d%d%d", &nn, &mm, &from, &to);
int v1, v2, dis, cc;
for (int i = 0; i < mm; ++i) {
scanf("%d%d%d%d", &v1, &v2, &dis, &cc);
if (graph[v1][v2].dis > dis) {
graph[v1][v2].dis = graph[v2][v1].dis = dis;
graph[v1][v2].cost = graph[v2][v1].cost = cc;
}
}
Dijkstra(from);
stack res;
int curr = to;
while (curr != from) {
res.push(curr);
curr = pre[curr];
}
printf("%d", from);
while (!res.empty()) {
printf(" %d", res.top());
res.pop();
}
printf(" %d %d", dist[to], cost[to]);
return 0;
}
1003 Emergency (25 分)
真 的 惨 ,看了半天半年前自己贴在上的写法。一行一行对照,终于发现错哪了。从前就是不知道总结,就会贴代码,才凉凉了吧。。。。。。
-
最短路径条数:应当继承当前pre的最短路径条数,而不是++或赋值为1☠️
- 题目中没有说明两个结点之间最多只有一条路,最好用邻接矩阵存储,方便直接存两点间直接路径中最短的。
- Dijkstra过程中break要谨慎,或者干脆就loop N次就好惹♀️
#include
#include
#include
using namespace std;
const int INF = 0x3fffffff;
int nn, mm, src, final, cnt_path[505] = {0}, gathering[505] = {0};
int teams[505];
int graph[505][505];
void dijkstra(int root) {
bool visited[505] = {false};
int dist[505];
fill(dist, dist + nn, INF);
dist[root] = 0;
cnt_path[root] = 1;
gathering[root] = teams[root];
for (int i = 0; i < nn; ++i) {
int min_dist = INF, index = -1;
for (int j = 0; j < nn; ++j) {
if (!visited[j] && dist[j] < min_dist) {
index = j;
min_dist = dist[j];
}
}
if (index != -1) {
visited[index] = true;
for (int j = 0; j < nn; ++j) {
if (!visited[j] && graph[index][j] != INF) {
int new_dist = dist[index] + graph[index][j];
if (new_dist == dist[j]) {
cnt_path[j] += cnt_path[index];
gathering[j] = max(gathering[j], gathering[index] + teams[j]);
} else if (new_dist < dist[j]) {
dist[j] = new_dist;
cnt_path[j] = cnt_path[index];
gathering[j] = gathering[index] + teams[j];
}
}
}
// if (index == final) break; 等长路径呢? break需谨慎!!
} else {
printf("ERROR. NOT CONNECTED.\n");
break;
}
}
}
int main() {
scanf("%d%d%d%d", &nn, &mm, &src, &final);
for (int i = 0; i < nn; ++i) {
scanf("%d", &teams[i]);
}
fill(graph[0], graph[0] + 505 * 505, INF);
int v1, v2, weight;
for (int i = 0; i < mm; ++i) {
scanf("%d%d%d", &v1, &v2, &weight);
if (graph[v1][v2] > weight) {
graph[v1][v2] = graph[v2][v1] = weight;
}
}
dijkstra(src);
printf("%d %d\n", cnt_path[final], gathering[final]);
return 0;
}