作者在跟着算法笔记学习Bellman-Ford算法时,使用该算法完成PTA的A1003题Emergency(代码如下)在统计最短路径时,由于贝尔曼福特算法会多次访问每一条边,这使得其会多次得到最短路径的前驱,故使用容器set去重。在统计最短路径数量时,使用迭代器遍历set,累加前驱的最短路径数量成功得到正确结果,而使用另一个方法在每次向set集合中插入元素时获取返回值,在插入成功时同时累加前驱结点的最短路径数量却无法总是得到正确结果,这是什么原因呢?
问题相关代码
//
// Created by BuGuWei on 2022/1/22.
//
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <set>
using namespace std;
const int MAXV = 500, INF = 0x3fffffff;
struct Node{
int v, dis;
Node(int _v, int _dis) : v(_v), dis(_dis){};
};
int n, m, c1, c2, weight[MAXV];
vector<Node> Adj[MAXV];
int d[MAXV], w[MAXV] = {0}, num[MAXV] = {0};
set<int> pre[MAXV];
using namespace std;
void bellman(int s);
int main() {
scanf("%d%d%d%d", &n, &m, &c1, &c2);
for(int i = 0; i < n; i++) {
scanf("%d", &weight[i]);
}
int a, b, l;
for(int j = 0; j < m; j++){
// Node* tempNode = new Node();
scanf("%d%d%d", &a, &b, &l);
Adj[a].push_back(Node(b, l));
Adj[b].push_back(Node(a, l));
}
// dijkstra(c1);
bellman(c1);
printf("%d %d\n", num[c2], w[c2]);
return 0;
}
void bellman(int s) {
fill(d, d + n, INF);
fill(w, w + n, 0);
fill(num, num + n, 0);
d[s] = 0;
w[s] = weight[s];
num[s] = 1;
for(int i = 0; i < n; i++) {
for(int u = 0; u < n; u++) {
for(int j = 0; j < Adj[u].size(); j++) {
int v = Adj[u][j].v, dis = Adj[u][j].dis;
if(d[u] + dis < d[v]) {
d[v] = d[u] + dis;
w[v] = w[u] + weight[v];
num[v] = num[u];
pre[v].clear();
pre[v].insert(u);
} else if(d[u] + dis == d[v]) {
if(w[u] + weight[v] > w[v]){
w[v] = w[u] + weight[v];
}
// 法一
if(pre[v].insert(u).second) {
num[v] += num[u];
}
// 法二
// pre[v].insert(u);
// num[v] = 0;
// set<int>::iterator it;
// for(it = pre[v].begin(); it != pre[v].end(); it++) {
// num[v] += num[*it];
// }
}
}
}
}
}
运行结果及报错内容
法一
法二
我的解答思路
即每次插入最短路径成功时计算当前最短路径数量。