L2-001 紧急救援 - Dijkstra堆优化最短路
PTA - L2 - 001 紧急救援
邻接表建图(稀疏图)
// 将结点b增加到结点a后
int h[N], w[N], e[N], ne[N], idx;
void add(int a, int b, int c)
{
w[idx] = c, e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
STL建图(稠密图、稀疏图)
// 重载小于号是为了便于dijsktra中大根堆的建立
struct edge
{
int x, w; // x 是结点编号,w是权值
bool operator<(const node& u)const { return w == u.w ? x < u.x : w > u.w; }
};
vector<edge> g[N];
思路:
本题不仅要求最短路,同时需要维护最短路上的救援队的最大数量、最短路的数量和最短路的路径。
- 出现最最短路的情况 dist[j] > dist[i] + w
- 更新最短路的路径
- 跟新救援队的数量
- 出现重复的路径 dist[j] == dist[i] + w
- 查找当前救援队的数量是否最大,若不是,则需要更新,更新后,最短路的路径也要更新。
- 同时,出现重复路径时,最短路径的数量 ++ 。
最后需要注意由于记录最短路的数组pre是记录前驱结点,最后输出时要倒序输出。
1. 邻接表建图
#include <bits/stdc++.h>
#define pii pair<int, int>
using namespace std;
const int N = 510;
int n, m, start, endd;
int h[N * N], e[N * N], ne[N * N], w[N * N], idx;
int dist[N];
int cnt[N], team[N], sum_team[N], pre[N];
bool st[N];
void insert(int a, int b, int c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
void dijkstra()
{
memset(dist, 0x3f, sizeof dist);
priority_queue<pii, vector<pii>, greater<pii>> heap;
dist[start] = 0;
heap.push({0, start});
cnt[start] = 1;
sum_team[start] = team[start];
while (heap.size())
{
auto t = heap.top();
heap.pop();
int ver = t.second, distance = t.first;
if (st[ver]) continue;
st[ver] = true;
for (int i = h[ver]; i != -1; i = ne[i])
{
int j = e[i];
if (dist[j] > w[i] + distance)
{
dist[j] = w[i] + distance;
cnt[j] = cnt[ver];
sum_team[j] = team[j] + sum_team[ver];
pre[j] = ver;
heap.push({dist[j], j});
}
else if (dist[j] == w[i] + distance)
{
cnt[j] += cnt[ver];
if (sum_team[j] < sum_team[ver] + team[j])
{
sum_team[j] = sum_team[ver] + team[j];
pre[j] = ver;
}
}
}
}
}
int main()
{
memset(h, -1, sizeof h);
cin >> n >> m >> start >> endd;
for (int i = 0; i < n; i++) cin >> team[i];
while (m--)
{
int a, b, c;
cin >> a >> b >> c;
insert(a, b, c);
insert(b, a, c);
}
dijkstra();
cout << cnt[endd] << " " << sum_team[endd] << endl;
vector<int> path;
for (int i = endd; i != start; i = pre[i])
{
path.push_back(i);
}
path.push_back(start);
for (int i = path.size() - 1; i >= 0; i--)
{
cout << path[i];
if (i != 0) cout << " ";
}
return 0;
}
2. STL建图
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 2000;
struct node
{
int x, w;
bool operator< (const node& u)const { return w == u.w ? x < u.x : w > u.w; }
};
vector<node> g[N];
int d[N], sum[N], sumt[N], pre[N], len, cnt[N];
void dijkstra(int st, int n)
{
memset(d, 0x3f, sizeof(int) * (n + 1));
priority_queue<node> hp;
d[st] = 0;
hp.push({st, d[st]});
cnt[st] = 1;
sum[st] = sumt[st];
bitset<N> vis;
while (hp.size())
{
auto x = hp.top().x;
hp.pop();
if (vis[x]) continue;
vis[x] = true;
for (auto &[v, w] : g[x])
{
if (d[v] > d[x] + w)
{
d[v] = d[x] + w;
cnt[v] = cnt[x];
sum[v] = sum[x] + sumt[v];
hp.push({v, d[v]});
pre[v] = x;
}
else if (d[v] == d[x] + w)
{
cnt[v] += cnt[x];
if (sum[v] < sum[x] + sumt[v])
{
sum[v] = sum[x] + sumt[v];
pre[v] = x;
}
}
}
}
}
int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n, m, s, d;
cin >> n >> m >> s >> d;
for (int i = 0; i < n; i++) cin >> sumt[i];
for (int i = 1; i <= m; i++)
{
int a, b, c;
cin >> a >> b >> c;
g[a].push_back({b, c});
g[b].push_back({a, c});
}
dijkstra(s, n);
cout << cnt[d] << " " << sum[d] << "\n";
vector<int> a;
for (int i = d; i != s; i = pre[i]) a.push_back(i);
int len = a.size();
cout << s << " ";
for (int i = len - 1; i >= 0; i--) cout << a[i] << " \n"[i == 0];
}