题目来源:团体程序设计天梯赛-练习集
题目地址:L2-001 紧急救援
题目描述
题目大意
题目给出一张图,其中包括道路连接的城市和它们的距离,以及每个城市救援队的数量,最后求从出发地城市到目的地城市的最短路径条数、经过城市能召集到的最多救援队数量和最后选择的路径,可以结合样例理解题意:
题目分析
其实这道题目就是常规的最短路径题目,和模板有所不同的是,在求最短路径还要兼顾救援队的数量和统计最短路径条数。主要需要理解以下两种情况下的统计:
-
当找更短的路径,即
dis[v] > dis[u] + E[u][i].second
时,
因为经过城市 去城市 是更优方案,所以在先前肯定没有统计到这条路径上的救援队数量,所示直接用到城市 后总的救援队数量加上城市 的救援队数量。此时 和 之间只有一条边相连,所以到 的最短路径数也就等于到 的最短路径数。 如下图所示,到城市 的最短路径数,不会因为和城市 之间的一条路径增加,所以等于到城市 的最短路径数,即 。
-
当找到与最短路径相等的路径,即
dis[v] == dis[u] + E[u][i].second
时,
需要判断能否在这个城市召集到更多的救援队,如果可以则更新救援队数量和经过的路径。由于是找到了长度一样的路径,相当多了一套到 的方案,所以到城市 总的最短路径数是要多加上到点 的最短路径数( )。如下图所示,例如从 到 ,除了经过城市 以外,又发现了经过城市 且距离相同的路,那么结果应该要加上到城市 的最短路径数量( ), 所以最终 。
代码如下
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
const int inf = 0x3f3f3f;
const int maxn =550;
int n, m, s, d;
/**
* w[i]表示i城市拥有的救援队数量
* sum[i]表示走到i城市可以召集到的救援队总数
* total[i]表示走到i城市的路线数量
*/
int w[maxn], sum[maxn], total[maxn];
/**
* dis[i]表示从源点到i点的最短距离
* path[i]表示到i点的前驱节点
*/
int dis[maxn], vis[maxn], path[maxn];
vector<pii> E[maxn];
/**
* 用dijstra算法求最短路径
*/
void dijstra(int s) {
memset(vis, 0, sizeof(vis));
//pair比较大小默认是先比较first,所以这里用first表示到源点的距离
//改变优先队列的优先级,让到源点距离短的节点优先级高
priority_queue<pii, vector<pii>, greater<pii> > q;
// 初始化
for (int i = 0; i < n; i++) dis[i] = inf;
dis[s] = 0;
sum[s] = w[s];
total[s] = 1;
q.push(pii(dis[s], s));
while (!q.empty()) {
int u = q.top().second;
q.pop();
if (vis[u]) continue;
vis[u] = 1;
for (int i = 0; i < E[u].size(); i++) {
int v = E[u][i].first;
if (dis[v] > dis[u] + E[u][i].second) {
dis[v] = dis[u] + E[u][i].second;
//直接加上该城市的救援队数量即可
sum[v] = sum[u] + w[v];
total[v] = total[u];
path[v] = u;
q.push(pii(dis[v], v));
} else if (dis[v] == dis[u] + E[u][i].second) {
//如果在路径长度相等的情况下,该路径救援队数量更多
if (sum[v] < sum[u] + w[v]) {
//更新救援队数量
sum[v] = sum[u] + w[v];
path[v] = u;
}
total[v] += total[u];
}
}
}
}
/**
* 用于输出经过的路径
*/
void output(int s, int d) {
if (s == d) {
printf("%d", s);
return ;
} else {
output(path[s], d);
printf(" %d", s);
}
}
int main()
{
scanf("%d %d %d %d", &n, &m, &s, &d);
for (int i = 0; i < n; i++) scanf("%d", &w[i]);
for (int i = 1; i <= m; i++) {
int x, y, t;
scanf("%d %d %d", &x, &y, &t);
//由于是无向图,所以两个方向的边都要加上
E[x].push_back(pair<int, int>(y, t));
E[y].push_back(pair<int, int>(x, t));
}
dijstra(s);
printf("%d %d\n", total[d], sum[d]);
output(d, s);
return 0;
}
如果本文对你有所帮助,别忘了点赞哦~