题目传送门

天梯赛习题l2-1:紧急救援


解题思路

为了练习链式前向星,这题使用链式前向星存储边数组。这题是dijkstra的变形算法。

dt数组存储到每个城市最多的救援队数目

pre数组存储起点到各个点最短路径的前缀,方便使用递归打印最短路径

这题要对dis[u] = dis[v] + edge[i].w的情况进行特判,判断该情况是否比之前救援队的数目多,如果增加,则需要更换最短路径。

注意这道题没有告诉M的数目,所以我们要开到最大的情况,也就是500 * 500

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 2005;
typedef pair<int, int> P;
struct Edge {
int next, to, w;
}edge[500 * 500 + 5]; //这题没有给出M的范围,所以我们要给定最大边值也就是500 * 500
int n, m, st, ed, cnt;
int dis[N], head[N], pre[N], team[N], dt[N], num[N]; //tot为队伍总数, num为最短路条数
bool vis[N];

inline void init()
{
cnt = 0;
memset(dis, INF, sizeof dis);
memset(head, -1, sizeof head);
memset(pre, 0, sizeof pre);
memset(dt, 0, sizeof dt);
}
inline void add(int u, int v, int w)
{
edge[cnt].w = w;
edge[cnt].to = v;
edge[cnt].next = head[u];
head[u] = cnt++;
}


void dijkstra()
{
priority_queue<P, vector<P>, greater<P> > q;
dis[st] = 0; dt[st] = team[st]; num[st] = 1; q.push({0, st});
while(q.size()) {
P now = q.top(); q.pop();
int u = now.second;
if(vis[u]) continue;
vis[u] = true;
for(int i = head[u]; ~i; i = edge[i].next) {
int v = edge[i].to;
if(!vis[v] && dis[v] >= dis[u] + edge[i].w) {
if(dis[v] == dis[u] + edge[i].w) {
num[v] += num[u];
if(dt[u] + team[v] > dt[v]) {
dt[v] = dt[u] + team[v];
pre[v] = u;
}
}
else {
dis[v] = dis[u] + edge[i].w;
dt[v] = dt[u] + team[v];
num[v] = num[u];
pre[v] = u;
}
q.push({dis[v], v});
}
}
}
}

void printPath(int v)
{
if(v == st) {
cout << v;
return;
}
printPath(pre[v]);
cout << " " << v;
}
int main()
{
int a, b, c;
cin >> n >> m >> st >> ed;
init();
for(int i = 0; i < n; ++i) cin >> team[i];
for(int i = 0; i < m; ++i) {
cin >> a >> b >> c;
add(a, b, c);
add(b, a, c);
}
dijkstra();
cout << num[ed] << " " << dt[ed] << endl;
printPath(ed);
return 0;
}