引论
本以为这是一道水题却因为考虑不周WA了半天,参考了博客https://blog.csdn.net/tiantangrenjian/article/details/19434417,感觉这题还是蛮不错的
题目链接
https://pintia.cn/problem-sets/994805342720868352/problems/994805523835109376
题目大意
给定一个无向图,求两点之间有几条最短路径,同时每个的都有一个权值,要求求出在最短路径的情况下权值最大为多少
分析
虽然加了个附加条件但总体来看还是单源最短路径问题,而且节点的范围非常小只有500,所以先跑一下dijstra,在松弛的时候加上对节点权值的处理,这样我们就能得到最后最大的权值;跑完dijstra后,各个节点到源节点的长度确定,这时从目标节点往源节点跑一遍DFS,只要满足G[u][v] + d[u] = d[v]我们就使该点处的条数加上dfs(u),初始化源节点的条数为1,这样dfs(t)就是我们求的最短路径的条数
我的误区
在刚开始的时候我没有写dfs只是设了一个cnt计数,遍历所有节点,当节点i不等于目标节点v且d[i] + G[i][v] = d[v]时,cnt++;这种做法的盲点在于使用这种方法只可以甄别出有几个从节点到目标节点长度相等,但是从其他节点到这个中间节点可能也有几条不同的路径,所以这种方法根本就没有办法找全所有的路径,下面给出一组样例
4 5 0 3
1 4 1 2
0 1 1
0 2 2
0 3 4
1 2 1
2 3 2
该样例的图形如下
(此图片及样例转自https://blog.csdn.net/tiantangrenjian/article/details/19434417)
参考代码
此代码仅供参考,希望大家独立实现算法,将此程序当做对拍程序使用
//题目https://pintia.cn/problem-sets/994805342720868352/problems/994805523835109376
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define N 550
#define INF 0x3f3f3f3f
int n, m, s, t;
int vis[N], cost[N], d[N], sum[N], dp[N];
int G[N][N];
void dijstra() {
memset(vis, 0, sizeof(vis));
memset(d, INF, sizeof(d));
d[s] = 0;
while (1) {
int v = -1;
for (int i = 0; i < n; ++i) {
if (vis[i]) continue;
if (v == -1 || d[v] > d[i] ) {
v = i;
}
}
if (v == -1) break;
vis[v] = 1;
for (int i = 0; i < n; ++i) {
if (i == v) continue;
if (d[i] > d[v] + G[i][v]) {
d[i] = d[v] + G[i][v];
sum[i] = sum[v] + cost[i];
} else if (d[i] == d[v] + G[i][v]) {
if (sum[i] < sum[v] + cost[i]) {
sum[i] = sum[v] + cost[i];
}
}
}
}
}
int dfs(int t) {
int &ans = dp[t];
if (ans) return ans;
for (int i = 0; i < n; ++i) {
if (d[i] + G[i][t] == d[t]) {
ans += dfs(i);
}
}
return ans;
}
int main() {
scanf("%d%d%d%d", &n, &m, &s, &t);
memset(G, INF, sizeof(G));
for (int i = 0; i < n; ++i) {
scanf("%d", &cost[i]);
sum[i] = cost[i];
}
for (int i = 0; i < m; ++i) {
int u, v, c;
scanf("%d%d%d", &u, &v, &c);
G[u][v] = G[v][u] = min(c, G[u][v]);
}
dijstra();
memset(dp, 0, sizeof(dp)); dp[s] = 1;
printf("%d %d\n", dfs(t), sum[t]);
return 0;
}