7-16 城市间紧急救援 (综合最短路练习)


7-16 城市间紧急救援 (综合最短路练习)

原创 已于 2023-10-05 21:38:12 修改 · 粉丝可见 · 306 阅读 · 0 · 0 · 本内容遵循CC 4.0 BY-SA版权协议 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 GEO检测 · 编辑
文章链接:https://blog.csdn.net/hacker_51/article/details/133579027

本题链接: PTA | 程序设计类实验辅助教学平台

题目:

样例:

cobol<br/>4 5 0 3<br/>20 30 40 10<br/>0 1 1<br/>1 3 2<br/>0 3 3<br/>0 2 2<br/>2 3 2<br/>
2 60
0 1 3

思路:

这道题是经典的综合最短路问题,

综合了 三种最短路方法, 1.求路径条数2.最短路多边权问题3.求最短路的路径

这里有个注意的点是,结点人数的的比较应该是更多更好。所以堆排序的时候,注意将结点人数总和多的排在前面。

代码详解如下:

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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <unordered_map>
#define endl '\n'
#define x first
#define y second
#define mk make_pair
#define int long long
#define NO puts("NO")
#define YES puts("YES")
#define umap unordered_map
#define INF 0x3f3f3f3f3f3f3f3f
#define All(x) (x).begin(),(x).end()
#pragma GCC optimize(3,"Ofast","inline")
#define ___G std::ios::sync_with_stdio(false),cin.tie(0), cout.tie(0)
using namespace std;
const int N = 2e6 + 10;
using PII = pair<int,int>;

struct Edge
{
int a; // 相关结点
int dis; // 相关最短距离
int peo; // 相关人数总和

// 构造相关结构体
inline Edge(int a,int dis,int peo)
{
this->a = a;
this->dis = dis;
this->peo = peo;
}

// 重载 < 比较符,建立堆排序规则
inline bool operator<(const Edge&t)const
{
// 优先排序最短路问题,最短路的排前面
if(dis != t.dis) return dis > t.dis;
// 如果最短路相同,优先排序人数多的在前面
return peo < t.peo;
}
};

int n,m,k,start,last;

int havePeo[N]; // 记录经过结点的拥有总救援人数
int dist[N]; // 记录最短路距离
int peo[N]; // 记录结点城市拥有的救援人数
bool st[N]; // 标记走动的结点城市
int tree[N]; // 记录结点城市由哪上一个结点城市相连的,求路径
int treeSum[N]; // 记录路径条数
vector<int>path; // 记录路径

int len,sum; // sum 是总救援人数,len 是路径结点个数

// 建立链表
int h[N],e[N],ne[N],w[N],idx;
inline void Add(int a,int b,int c)
{
e[idx] = b,w[idx] = c,ne[idx] = h[a],h[a] = idx++;
}

inline void Dijkstra()
{
// 初始化最短距离和路径条数和总救援人数
memset(dist,INF,sizeof dist);
dist[start] = 0;
havePeo[start] = 0;
treeSum[start] = 1;

// 建立堆
priority_queue<Edge>q;

// 存储起点相关结构体
q.push(Edge(start,0,0));

// 开始堆排序的 Dijkstra
while(q.size())
{
// 获取优先走动的相关结构体
Edge now = q.top();
q.pop();

int a = now.a; // 获取当前走动的结点
int dis = now.dis; // 获取当前记录的最短路距离
int p = now.peo; // 获取当前记录的拥有救援总人数

// 如果当前结点走动过,不必再继续更新走动
if(st[a]) continue;
st[a] = true; // 标记当前走动的结点城市

for(int i = h[a];i != -1;i = ne[i])
{
int j = e[i]; // 获取走动的结点
// 如果 j 走动结点的最短路距离方案大于 当前解 a 结点 到 j 结点的距离
if(dist[j] > dis + w[i])
{
dist[j] = dis + w[i];// 更新最短路
treeSum[j] = treeSum[a]; // 继承路径条数
tree[j] = a; // 记录当前 j 结点城市是由 a 结点走动得来的
havePeo[j] = p + peo[j]; // 记录走动到 j 结点路径拥有的总救援人数
}else // 如果最短距离方案 与当前 a 结点走动到 j 结点最短距离相同
if(dist[j] == dis + w[i])
{
// 累计路径条数
treeSum[j] += treeSum[a];

// 如果当前 j 结点拥有的总救援人数方案 比 a 到 j 结点的总救援人数 少,那么更新路径
if(havePeo[j] < p + peo[j])
{
havePeo[j] = p + peo[j]; // 更新走动到 j 结点路径拥有的总救援人数
tree[j] = a; // 更新结点城市路径
}
}
// 存储走动的 j 结点,进行下一次的走动比较
q.push(Edge(j,dist[j],havePeo[j]));
}
}
}

// 获取最短路的路径
void getPath(int now)
{
// 递归到了起点,开始回溯取路径
if(now == start)
{
// 存储路径
path.emplace_back(now);
++len; // 记录路径点个数
sum += peo[now]; // 累加救援人数
return ;
}

// 递归求路径
getPath(tree[now]);

// 存储路径
path.emplace_back(now);
++len; // 记录路径点个数
sum += peo[now]; // 累加救援人数
}

inline void solve()
{
// 初始化链表
memset(h,-1,sizeof h);

cin >> n >> k >> start >> last;

for(int i = 0;i < n;++i)
{
cin >> peo[i];
}

while(k--)
{
int a,b,c;
cin >> a >> b >> c;

// 相连两个城市
Add(a,b,c);
Add(b,a,c);
}

// Dijkstra 求值
Dijkstra();

// 由终点回溯递归获取路径
getPath(last);

// 输出 路径条数,和 救援人员总人数
cout << treeSum[last] << ' ' << sum << endl;

// 输出救援路径
for(int i = 0;i < len;++i)
{
if(i) cout << ' ';
cout << path[i];
}
}

signed main()
{
// freopen("a.txt", "r", stdin);
___G;
int _t = 1;
// cin >> _t;
while (_t--)
{
solve();
}

return 0;
}

最后提交:


觉得不错的话,给点打赏吧 ୧(๑•̀⌄•́๑)૭

微信二维码

wechat pay

支付宝二维码

ali pay

7-16 城市间紧急救援 (综合最短路练习)
http://blog.angindem.cn/2023/10/05/Angindem-CSDN博客/085_85/
作者
Angindem
发布于
2023年10月5日
许可协议