题目描述
求最短路和次短路的条数
算法
(最短路) O(T∗mlog(n))
- 设状态dist[i][0,1]表示初始城市S到城市i的最短距离和次短距离,cnt[i][0,1]表示城市S到城市i的最短路径和次短路经的条数
- 初始时,dist[S][0]为0,cnt[S][0]为1(其余都初始化成正无穷,初始时S不存在次短路)
- Dijkstra算法枚举城市t可通往的城市j时,有四种情况:
- dist[j][0]>dist[v][type]+w[i]:当前最短路变成次短路,更新最短路,将最短路和次短路加入优先队列
- dist[j][0]=dist[v][type]+w[i]:找到一条新的最短路,更新最短路条数
- dist[j][1]>dist[v][type]+w[i]:找到一条更短的次短路,覆盖掉当前次短路,加入优先队列
- dist[j][1]=dist[v][type]+w[i]:找到一条新的次短路,更新次短路条数
- 到F城市的次短路径如果比最短路径恰好多1,则把这样的路径条数加到答案里
- C++优先队列大根堆需要重载小于号,小根堆需要重载大于号
时间复杂度
- T组数据,每组需要做一次堆优化的Dijkstra算法,故总时间复杂度为O(T∗mlog(n))。
C++ 代码
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int N = 1010, M = 100010;
int cases, n, m, S, F;
int h[N], e[M], w[M], ne[M], idx;
int dist[N][2], cnt[N][2];
bool st[N][2];
struct Node {
int id, type, dist;
bool operator> (const Node& node) const {
return dist > node.dist;
}
};
void add(int a, int b, int c) {
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}
int dijkstra() {
memset(st, 0, sizeof st);
memset(dist, 0x3f, sizeof dist);
dist[S][0] = 0, cnt[S][0] = 1;
priority_queue<Node, vector<Node>, greater<Node>> q;
q.push({S, 0, 0});
while (q.size()) {
auto t = q.top(); q.pop();
int v = t.id, type = t.type;
if (st[v][type]) continue;
st[v][type] = true;
for (int i = h[v]; ~i; i = ne[i]) {
int j = e[i];
if (dist[j][0] > dist[v][type] + w[i]) {
dist[j][1] = dist[j][0], cnt[j][1] = cnt[j][0];
q.push({j, 1, dist[j][1]});
dist[j][0] = dist[v][type] + w[i], cnt[j][0] = cnt[v][type];
q.push({j, 0, dist[j][0]});
}
else if (dist[j][0] == dist[v][type] + w[i]) cnt[j][0] += cnt[v][type];
else if (dist[j][1] > dist[v][type] + w[i]) {
dist[j][1] = dist[v][type] + w[i], cnt[j][1] = cnt[v][type];
q.push({j, 1, dist[j][1]});
}
else if (dist[j][1] == dist[v][type] + w[i]) cnt[j][1] += cnt[v][type];
}
}
int ret = cnt[F][0];
if (dist[F][0] + 1 == dist[F][1]) ret += cnt[F][1];
return ret;
}
int main() {
cin >> cases;
while (cases -- ) {
cin >> n >> m;
memset(h, -1, sizeof h);
idx = 0;
while (m -- ) {
int a, b, c; cin >> a >> b >> c;
add(a, b, c);
}
cin >> S >> F;
cout << dijkstra() << endl;
}
return 0;
}
%%%
很详细👍
有个问题
if (dist[j][0] > dist[v][type] + w[i]) {
dist[j][1] = dist[j][0], cnt[j][1] = cnt[j][0];
q.push({j, 1, dist[j][1]});
这个不是之前已经入过队了吗?为什么还要入一次呢?
谢谢
dijkstra也会入队多次呀,但是只会取第一次出队结果
谢谢qwq
小意思
其实是不需要的,我的代码里就没有这行。按理说只有被更新的点才需要再次入队。若想拓展可以看一下这道题:https://cses.fi/problemset/task/1196
你代码不是也一样有这行吗
我认为是else的原因,因为一开始推入队更新的是最短路,所以这个理论上可以成为我的次短路的路径并没有拿来去更新我的次短路,然后再从次短路去尝试能不能去更新别的路的次短路。所以这里我需要把他推进去去尝试更新我别的次短路。
赞
bool operator> (const Ver &W) const {
return dist > W.dist;
}
dij用的小根堆,这里为什么要重载的是大于号
因为它用的全是大于号呀,他可能打错了,这里应该是重载大于号
dl 好强啊,我看了你很多篇题解,很喜欢你的总结。Orz。
谢谢