题目链接
洛谷: https://www.luogu.com.cn/problem/P10947
AcWing: https://www.acwing.com/problem/content/description/385/
关联知识
单源最短路与Dijkstra算法
思路分析
本题旨在要求我们求出最短路和次最短路(且总距离仅比最短路小于1) 的路径数量综合。对于本题目,笔者建议毫无头绪的同学可以先尝试这一题:最短路计数。本文的题目实际上是对该题的扩展延伸。
最短路计数要求我们统计最短路的条数,在这题中,我们采用了分类讨论的方式,想到在更新最短路的同时也更新最短路的条数。用数组 c n t cnt cnt 记录到达当前点的最短路的条数, d i s t a n c e distance distance 为到达当前点的最短路, w w w 为当前点到领接点的边权,如果最短路可以被更新,那么 c n t cnt cnt 数组则被更新成新路径所记录的条数;如果不满足上面一种情况,但是当前领接点所记录的最短路恰好等于 d i s t a n c e + w distance+w distance+w,那么说明这是一条新出现的最短路路径, 将两条路径的 c n t cnt cnt 相加即可。
而在观光一题中,题目要求多了一个——求距离比最短路小 1 的次最短路的条数。我们可以从最短路计数一题中得到启发,依然采用分类讨论的方式。为了实时记录最短路和次最短路两个数据,我们可以在经典最短路算法的数组的基础上扩展为二维数组,用 0 表示最短路,用 1 表示次最短路(见代码)
- 情况一:当前记录最短路
>
d
i
s
t
a
n
c
e
+
w
>distance+w
>distance+w
这是优先级最高的情况,如果当前记录的最短路可以被更新,那么当前记录的最短路距离则可以变成次最短路。同时,令次最短路的条数等于最短路的条数,将最短路条数同步进行更新。 - 情况二:当前记录最短路
=
d
i
s
t
a
n
c
e
+
w
=distance+w
=distance+w
如果不满足情况一,但是满足此情况,则说明出现了一条新的可以走出最短路的路径,将两种路径的 c n t cnt cnt 进行合并即可。 - 情况三:当前记录次最短路
>
d
i
s
t
a
n
c
e
+
w
>distance+w
>distance+w
如果不满足情况一、二,但是满足此情况,则说明 d i s t a n c e + w distance+w distance+w 虽然不足以成为最短路,但是可以作为次最短路。则将次最短路的数据类比情况一进行更新。 - 情况四:当前记录次最短路
=
d
i
s
t
a
n
c
e
+
w
=distance+w
=distance+w
如果前三种情况都不满足,但是 d i s t a n c e + w distance+w distance+w 恰好等于当前记录的次最短路,则说明出现了一条新的可以走出次最短路的路径,将两种路径的 c n t cnt cnt 类比情况二进行合并即可。
如果一种情况都不满足,说明对最短路和次最短路不构成影响,不用管。
AC代码
细节点提示:
1.数据有多组,该重置的数组、数据一定要记得重置;
2.分类情况较多,不要混淆最短路和次最短路,更不要弄错数据;
3.本题的次最短路必须满足比最短路距离小 1,一定要做特判。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
const int N = 1100, M = 1e4 + 10, INF = 0x3f3f3f3f;
struct Edge
{
int u, type, dist;
bool operator> (const Edge &a) const
{
return dist > a.dist;
}
};
int T, n, m, s, f;
int st[N][2], dis[N][2], cnt[N][2];
int ne[M], h[N], e[M], w[M], idx;
void add(int a, int b, int c)
{
w[idx] = c;
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
return;
}
int dijkstra()
{
memset(st, 0, sizeof st);
memset(dis, INF, sizeof dis);
memset(cnt, 0, sizeof cnt);
//类型0为最短路,类型1为次短路
dis[s][0] = 0;
cnt[s][0] = 1;
priority_queue<Edge, vector<Edge>, greater<Edge>> q;
q.push({s, 0, 0});
while (!q.empty())
{
auto now = q.top();
q.pop();
int nowu = now.u, nowtype = now.type, nowdis = now.dist;
if (st[nowu][nowtype]) continue;
st[nowu][nowtype] = true;
for (int i = h[nowu]; i != -1; i = ne[i])
{
int j = e[i];
//当前值可以更新最小值
if (dis[j][0] > nowdis + w[i])
{
cnt[j][1] = cnt[j][0];
dis[j][1] = dis[j][0];
q.push({j, 1, dis[j][1]});
dis[j][0] = nowdis + w[i];
cnt[j][0] = cnt[nowu][nowtype];
q.push({j, 0, dis[j][0]});
}
else if (dis[j][0] == nowdis + w[i])
{
//当前值是一条新路线的等距离的最短路
cnt[j][0] += cnt[nowu][nowtype];
}
else if (dis[j][1] > nowdis + w[i])
{
//当前值虽然不足以成为最短路,但是可以成为次短路
dis[j][1] = nowdis + w[i];
cnt[j][1] = cnt[nowu][nowtype];
q.push({j, 1, dis[j][1]});
}
else if (dis[j][1] == nowdis + w[i])
{
cnt[j][1] += cnt[nowu][nowtype];
}
}
}
int res = cnt[f][0];
if (dis[f][1] == dis[f][0] + 1) res += cnt[f][1];
return res;
}
int main()
{
scanf("%d", &T);
while (T--)
{
memset(h, -1, sizeof h);
idx = 0;
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++)
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
}
scanf("%d%d", &s, &f);
int ans = dijkstra();
printf("%d\n", ans);
}
return 0;
}

203

被折叠的 条评论
为什么被折叠?



