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


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

    题目:

    样例:

    输入
    1. 4 5 0 3
    2. 20 30 40 10
    3. 0 1 1
    4. 1 3 2
    5. 0 3 3
    6. 0 2 2
    7. 2 3 2

    输出
    2 60
    0 1 3

    思路:

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

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

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

    代码详解如下:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #define endl '\n'
    7. #define x first
    8. #define y second
    9. #define mk make_pair
    10. #define int long long
    11. #define NO puts("NO")
    12. #define YES puts("YES")
    13. #define umap unordered_map
    14. #define INF 0x3f3f3f3f3f3f3f3f
    15. #define All(x) (x).begin(),(x).end()
    16. #pragma GCC optimize(3,"Ofast","inline")
    17. #define ___G std::ios::sync_with_stdio(false),cin.tie(0), cout.tie(0)
    18. using namespace std;
    19. const int N = 2e6 + 10;
    20. using PII = pair<int,int>;
    21. struct Edge
    22. {
    23. int a; // 相关结点
    24. int dis; // 相关最短距离
    25. int peo; // 相关人数总和
    26. // 构造相关结构体
    27. inline Edge(int a,int dis,int peo)
    28. {
    29. this->a = a;
    30. this->dis = dis;
    31. this->peo = peo;
    32. }
    33. // 重载 < 比较符,建立堆排序规则
    34. inline bool operator<(const Edge&t)const
    35. {
    36. // 优先排序最短路问题,最短路的排前面
    37. if(dis != t.dis) return dis > t.dis;
    38. // 如果最短路相同,优先排序人数多的在前面
    39. return peo < t.peo;
    40. }
    41. };
    42. int n,m,k,start,last;
    43. int havePeo[N]; // 记录经过结点的拥有总救援人数
    44. int dist[N]; // 记录最短路距离
    45. int peo[N]; // 记录结点城市拥有的救援人数
    46. bool st[N]; // 标记走动的结点城市
    47. int tree[N]; // 记录结点城市由哪上一个结点城市相连的,求路径
    48. int treeSum[N]; // 记录路径条数
    49. vector<int>path; // 记录路径
    50. int len,sum; // sum 是总救援人数,len 是路径结点个数
    51. // 建立链表
    52. int h[N],e[N],ne[N],w[N],idx;
    53. inline void Add(int a,int b,int c)
    54. {
    55. e[idx] = b,w[idx] = c,ne[idx] = h[a],h[a] = idx++;
    56. }
    57. inline void Dijkstra()
    58. {
    59. // 初始化最短距离和路径条数和总救援人数
    60. memset(dist,INF,sizeof dist);
    61. dist[start] = 0;
    62. havePeo[start] = 0;
    63. treeSum[start] = 1;
    64. // 建立堆
    65. priority_queueq;
    66. // 存储起点相关结构体
    67. q.push(Edge(start,0,0));
    68. // 开始堆排序的 Dijkstra
    69. while(q.size())
    70. {
    71. // 获取优先走动的相关结构体
    72. Edge now = q.top();
    73. q.pop();
    74. int a = now.a; // 获取当前走动的结点
    75. int dis = now.dis; // 获取当前记录的最短路距离
    76. int p = now.peo; // 获取当前记录的拥有救援总人数
    77. // 如果当前结点走动过,不必再继续更新走动
    78. if(st[a]) continue;
    79. st[a] = true; // 标记当前走动的结点城市
    80. for(int i = h[a];i != -1;i = ne[i])
    81. {
    82. int j = e[i]; // 获取走动的结点
    83. // 如果 j 走动结点的最短路距离方案大于 当前解 a 结点 到 j 结点的距离
    84. if(dist[j] > dis + w[i])
    85. {
    86. dist[j] = dis + w[i];// 更新最短路
    87. treeSum[j] = treeSum[a]; // 继承路径条数
    88. tree[j] = a; // 记录当前 j 结点城市是由 a 结点走动得来的
    89. havePeo[j] = p + peo[j]; // 记录走动到 j 结点路径拥有的总救援人数
    90. }else // 如果最短距离方案 与当前 a 结点走动到 j 结点最短距离相同
    91. if(dist[j] == dis + w[i])
    92. {
    93. // 累计路径条数
    94. treeSum[j] += treeSum[a];
    95. // 如果当前 j 结点拥有的总救援人数方案 比 a 到 j 结点的总救援人数 少,那么更新路径
    96. if(havePeo[j] < p + peo[j])
    97. {
    98. havePeo[j] = p + peo[j]; // 更新走动到 j 结点路径拥有的总救援人数
    99. tree[j] = a; // 更新结点城市路径
    100. }
    101. }
    102. // 存储走动的 j 结点,进行下一次的走动比较
    103. q.push(Edge(j,dist[j],havePeo[j]));
    104. }
    105. }
    106. }
    107. // 获取最短路的路径
    108. void getPath(int now)
    109. {
    110. // 递归到了起点,开始回溯取路径
    111. if(now == start)
    112. {
    113. // 存储路径
    114. path.emplace_back(now);
    115. ++len; // 记录路径点个数
    116. sum += peo[now]; // 累加救援人数
    117. return ;
    118. }
    119. // 递归求路径
    120. getPath(tree[now]);
    121. // 存储路径
    122. path.emplace_back(now);
    123. ++len; // 记录路径点个数
    124. sum += peo[now]; // 累加救援人数
    125. }
    126. inline void solve()
    127. {
    128. // 初始化链表
    129. memset(h,-1,sizeof h);
    130. cin >> n >> k >> start >> last;
    131. for(int i = 0;i < n;++i)
    132. {
    133. cin >> peo[i];
    134. }
    135. while(k--)
    136. {
    137. int a,b,c;
    138. cin >> a >> b >> c;
    139. // 相连两个城市
    140. Add(a,b,c);
    141. Add(b,a,c);
    142. }
    143. // Dijkstra 求值
    144. Dijkstra();
    145. // 由终点回溯递归获取路径
    146. getPath(last);
    147. // 输出 路径条数,和 救援人员总人数
    148. cout << treeSum[last] << ' ' << sum << endl;
    149. // 输出救援路径
    150. for(int i = 0;i < len;++i)
    151. {
    152. if(i) cout << ' ';
    153. cout << path[i];
    154. }
    155. }
    156. signed main()
    157. {
    158. // freopen("a.txt", "r", stdin);
    159. ___G;
    160. int _t = 1;
    161. // cin >> _t;
    162. while (_t--)
    163. {
    164. solve();
    165. }
    166. return 0;
    167. }

    最后提交:

  • 相关阅读:
    netty系列之:netty对marshalling的支持
    支付官方解析
    MySQL碎片整理小节--实例演示
    记录一次并发情况下的redis导致服务假死的问题
    基于遗传算法的自主式水下潜器路径规划问题附Matlab代码
    STM32学习和实践笔记(20):定时器
    Prime Protocol宣布在Moonbeam上的跨链互连应用程序
    论文学习记录随笔 多标签之LSML
    CentOS 8迁移Rocky Linux 8手记
    ARM/DSP+FPGA运动控制机器视觉控制器方案定制
  • 原文地址:https://blog.csdn.net/hacker_51/article/details/133579027