次短路条数。
一个有向图,给定起点终点,问你次短路条数,保证有次短路,保证没有环路。
按理说这道题没什么可说的,但是,这道题有一个很大的坑,就是会有权值为零的边。这是我看了这道题的Discuss才知道的,感谢。
求条数的问题对零环敏感,这我们知道,但是这道题说了保证没有环路,所以本来不用担心。但是,求条数对零边也敏感(不管你是求最短路条数还是第k短路条数)。为什么?
问题出在else if (!vis[0][n] && mind + w == d[0][n])
和else if (!vis[1][n] && mind + w == d[1][n])
这两个情况下。假设当前边的w
为零,而且当前枚举的中介点mind
等于d[][n]
,那么你必须保证当前中介点u
一定能更新到n
,也就是说现在vis[][n]
一定还是false
。
然而,两个相等的d
值在优先队列里出队的次序是未知的。
你可能会想到删去!vis[][]
的判断。但是,没有用,依然WA。因为即使那个n
点的num
正确了,但是n
点先出队访问它自己的邻接点了,它已经把自己错误的num
值传递出去了。于事无补。
还可能会想到记录每条零边,然后当两个点的d
相等时,这两点间若有零边(不可能各有一条零边)则选择零边的前驱点先出队。很遗憾,这样依旧WA。为什么?且看下图:
第一种情况能应付,但第二种你应付不了。所以,最后我们选择先拓扑排序一遍(拓扑排序的前提是有向无环图(DAG)),给每个点一个编号,若A
到B
有路径,则A
的编号一定比B
小,反之不一定成立。
然后,我们改变优先队列的出队逻辑,当两点的d
相等时,选择拓扑排序编号小的点先出队。(优先队列的写法是反着来的)
上面两个图的测试用例在代码最后。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <string>
#include <queue>
#include <utility>
using namespace std;
const int INF = 1e9;
const int MAXN = 50;
int N, M, S, E;
vector<pair<int, int>> v[MAXN];
int d[2][MAXN];
bool vis[2][MAXN];
int num[2][MAXN];
//int pre[MAXN][MAXN];
int degree[MAXN];
int no[MAXN];
struct Node
{
int n, d, id;
Node(int _n, int _d, int _id) :n(_n), d(_d), id(_id) {}
bool operator<(const Node& n0) const
{
//if (d == n0.d && pre[n][n0.n] != -1) // 此处 pre[n][n0.n] != -1 一定要加,这样才能过测试1,但过不了测试2
// return n != pre[n][n0.n];
if (d == n0.d)
return no[n] > no[n0.n];
return d > n0.d;
}
};
void init()
{
for (int i = 0; i < N; i++) v[i].clear();
//memset(pre, -1, sizeof pre); //
memset(degree, 0, sizeof degree);
memset(no, 0, sizeof no);
}
void dijkstra(int s)
{
memset(vis, 0, sizeof vis);
memset(num, 0, sizeof num);
fill(d[0], d[0] + N, INF);
fill(d[1], d[1] + N, INF);
d[0][s] = 0;
num[0][s] = 1;
priority_queue<Node> q;
q.push(Node(s, d[0][s], 0));
for (; !q.empty();)
{
Node x = q.top();
q.pop();
int mind = x.d;
int u = x.n;
int id = x.id;
if (mind == INF) continue;
if (vis[id][u]) continue;
vis[id][u] = true;
for (int i = 0; i < v[u].size(); i++)
{
int n = v[u][i].first;
int w = v[u][i].second;
if (!vis[0][n] && mind + w < d[0][n])
{
d[1][n] = d[0][n];
num[1][n] = num[0][n];
d[0][n] = mind + w;
num[0][n] = num[id][u];
q.push(Node(n, d[1][n], 1));
q.push(Node(n, d[0][n], 0));
}
else if (!vis[0][n] && mind + w == d[0][n])
{
num[0][n] += num[id][u];
}
else if (!vis[1][n] && mind + w < d[1][n])
{
d[1][n] = mind + w;
num[1][n] = num[id][u];
q.push(Node(n, d[1][n], 1));
}
else if (!vis[1][n] && mind + w == d[1][n])
{
num[1][n] += num[id][u];
}
}
}
}
void topo()
{
int cnt = 0;
queue<int> q;
for (int i = 0; i < N; i++)
if (degree[i] == 0) q.push(i);
for (; !q.empty();)
{
int x = q.front();
q.pop();
no[x] = cnt++;
for (int i = 0; i < v[x].size(); i++)
{
int n = v[x][i].first;
degree[n]--;
if (degree[n] == 0)
q.push(n);
}
}
}
int main()
{
int a, b, c;
for (; ~scanf("%d%d%d%d", &N, &M, &S, &E);)
{
init();
for (int i = 0; i < M; i++)
{
scanf("%d%d%d", &a, &b, &c);
v[a].push_back(make_pair(b, c));
//if (c == 0) pre[a][b] = pre[b][a] = a; //
degree[b]++;
}
topo();
dijkstra(S);
printf("%d %d\n", d[1][E], num[1][E]);
}
return 0;
}
/*测试1
4 5 0 2
0 2 1
0 1 1
1 2 2
1 3 2
3 2 0
3 2*/
/*测试2
5 6 0 2
0 2 1
0 1 1
1 2 2
1 3 2
3 4 0
4 2 0
3 2*/