題目來源:團體程序設計天梯賽-練習集
題目地址: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;
}
如果本文對你有所幫助,別忘了點贊哦~