这个题目整合了我们最短路用到的很多技能
如何统计最短路径的方案数呢,这就需要我们另外开一个全局数组
如何打印路径呢,还是开一个全局的数组,记录前一个是啥就行
简单的来说,要增加啥新的功能,直接多开全局变量就行,没必要写在优先队列里面,不好转换
#include<bits/stdc++.h>
using namespace std;
const int N = (int)505;
int n,m,s,d;
int num[N];
// 没有说明有多少条边
struct node{
int v,w;
};
vector<node> ed[N];
vector<int> path(N,-1);
vector<int> cnt(N,0);
vector<int> shu(N,0);
void fun(){
vector<int> dis(n,0x3f3f3f3f);
vector<int> vis(n,0);
priority_queue<pair<int,int>,vector<pair<int,int>>,greater<>> q;
q.push({0,s});
cnt[s] = 1;
shu[s] = num[s];
dis[s] = 0;
while(q.size()){
auto now = q.top();q.pop();
int d = now.first, u = now.second;
if(vis[u]) continue;
vis[u] = 1;
for(int i=0;i<ed[u].size();i++){
int v = ed[u][i].v, w = ed[u][i].w;
if(d+w<dis[v]){ // 这里还是很正常的最短路
dis[v] = d + w;
cnt[v] = cnt[u];
shu[v] = shu[u] + num[v];
path[v] = u;
q.push({dis[v],v});
}
else if(d+w==dis[v]){ // 如果距离相等就需要加上这条路径上的方案数
cnt[v] += cnt[u];
if(shu[v]<shu[u]+num[v]){ // 相同距离下,如果救援队的数量更多,就采取这条新的路径
path[v] = u;
shu[v] = shu[u] + num[v];
}
}
}
}
}
int main(){
cin >> n >> m >> s >> d;
for(int i=0;i<n;i++){
cin >> num[i];
}
int u,v,w;
for(int i=1;i<=m;i++){
cin>>u>>v>>w;
ed[u].push_back({v,w});
ed[v].push_back({u,w});
}
fun();
cout << cnt[d] << " " << shu[d] << endl;
vector<int> ans;
int now = d;
while(path[now]!=-1){
ans.push_back(path[now]);
now = path[now];
}
for(int i=ans.size()-1;i>=0;i--){
cout << ans[i] << " ";
}
cout << d;
return 0;
}
要弄清楚的就是距离相同的时候,如何进行转换,距离相同的时候,救援队的数量就是我们需要考虑的因素,我们直接采取新的路径就行
可能有的疑惑是为什么路径相等的时候不再次将这个节点push到堆中,这是因为我们的堆中肯定存在这个节点了,且一定没有遍历到,这是因为我们的当前的距离是d , 而v节点的距离dis[v] == d + w,这么说dis[v]大于d,那么v节点一定存在于堆中,我们就没必要重复push进去了
再看一个进阶版本的
这种题目就一个套路,就是在遍历每一个节点的时候进行查看距离是否和当前标记的一样,一样的话就处理下一个优先级的条件,
要注意的是不能漏掉条件,转换的时候要写好会变化的条件
#include<bits/stdc++.h>
using namespace std;
int n,k;
unordered_map<string,int> na2num;
unordered_map<int,string> num2na;
string start,en;
int cnt = 0;
const int N = (int)205;
int arnum[N];
int vis[N]; // 是否访问过
int pre[N]; // 前面一个是谁
int peonum[N]; // 杀敌人数
int citynum[N]; // 城镇数量
int fanan[N];
int dis[N]; // 距离
struct node{
int v,juli;
};
vector<node> edges[N];
int get_num(string name){
if(na2num[name]) return na2num[name];
na2num[name] = ++cnt;
num2na[cnt] = name;
return cnt;
}
int main(){
cin >> n >> k >> start >> en;
int ns = get_num(start);
int ne = get_num(en);
for(int i=1;i<n;i++){
string b; int c;
cin >> b >> c;
arnum[get_num(b)] = c;
}
for(int i=1;i<=k;i++){
string b,c;int d;
cin >> b >> c >> d;
edges[get_num(b)].push_back({get_num(c),d});
edges[get_num(c)].push_back({get_num(b),d});
}
for(int i=1;i<=n;i++){
dis[i] = 0x3f3f3f3f;
}
priority_queue<pair<int,int>> q;
q.push({0,ns});
dis[ns] = 0;
peonum[ns] = arnum[ns];
fanan[ns] = 1;
while (q.size()){
int u = q.top().second, d = -q.top().first;
q.pop();
if(vis[u]) continue;
vis[u] = 1;
for(int i=0;i<edges[u].size();i++){
int v = edges[u][i].v;
int juli = edges[u][i].juli;
if(dis[u]+juli==dis[v]){ // 这里用d+juli 也是可以的,因为第一个进到这里的,一定是d == dis[u]
fanan[v] += fanan[u];
if(citynum[u]+1>citynum[v]){
pre[v] = u;
citynum[v] = citynum[u] + 1;
peonum[v] = peonum[u] + arnum[v];
}else if(citynum[u]+1==citynum[v]){
if(peonum[u]+arnum[v]>peonum[v]){
pre[v] = u;
peonum[v] = peonum[u] + arnum[v];
}
}
}else if(d+juli<dis[v]){
fanan[v] = fanan[u];
dis[v] = d + juli;
citynum[v] = citynum[u] + 1;
peonum[v] = peonum[u] + arnum[v];
pre[v] = u;
q.push({-dis[v],v});
}
}
}
vector<string> ans;
int now = ne;
while(now){
ans.push_back(num2na[now]);
now = pre[now];
}
for(int i=ans.size()-1;i>=0;i--){
cout << ans[i];
if(i!=0){
cout << "->";
}
}
cout << endl;
cout << fanan[ne] << " " << dis[ne] << " "<< peonum[ne] << endl;
}