欢迎访问 My Luogu Space。
【题目大意】
有一张 n (n <= 2000) 个点 m (m <= n*(n-1)/2) 条边的有向图,每个边的边权为 c (c<=10) 。请求出从 1 到 n 的最短路以及最短路的条数。
【题解】
spfa / dij
直接用 spfa 或者 dij 求最短路(本题解采用 spfa),在求最短路的时候多统计一个数组 cnt[i] 表示从 1 到 i 的最短路有几条。
初始化 cnt[1] = 1,每次更新多增加一下操作:
- 如果 dis[x]+w[i] == dis[e[i]],那么 cnt[e[i]] += cnt[x];
- 如果 dis[x]+w[i] < dis[e[i],那么 dis[e[i]] = dis[x]+w[i], cnt[e[i]] = cnt[x];
并且每次更新完一个点之后,需要把当前点的 cnt[i] 清零,否则会重复计算。
【代码】
#include <bits/stdc++.h>
using namespace std;
const int MN = 2000+10;
const int INF = 0x3f3f3f3f;
int n, m, dis[MN], cnt[MN], f[MN];
int t[MN][MN];
int spfa(){
memset(dis, INF, sizeof dis);
queue<int> Q; Q.push(1);
dis[1] = 0, cnt[1] = 1;
while(!Q.empty()){
int x = Q.front(); Q.pop();
f[x] = 0;
if(x==n) continue;
for(int i=1; i<=n; ++i){
if(dis[x]+t[x][i]==dis[i])
cnt[i] += cnt[x];
else if(dis[x]+t[x][i]<dis[i]){
dis[i] = dis[x]+t[x][i];
cnt[i] = cnt[x];
}
if(cnt[i] && !f[i])
f[i] = 1, Q.push(i);
}
cnt[x] = 0;
}
return dis[n];
}
int main(){
scanf("%d%d", &n, &m);
memset(t, INF, sizeof t);
for(int i=1; i<=m; ++i){
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
t[a][b] = min(t[a][b], c);
}
if(spfa()==INF) puts("No answer");
else printf("%d %d", dis[n], cnt[n]);
return 0;
}