最短路的方案数+打印路径

这个题目整合了我们最短路用到的很多技能
如何统计最短路径的方案数呢,这就需要我们另外开一个全局数组
如何打印路径呢,还是开一个全局的数组,记录前一个是啥就行

简单的来说,要增加啥新的功能,直接多开全局变量就行,没必要写在优先队列里面,不好转换


题目地址

在这里插入图片描述

#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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wniuniu_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值