7-4 超级玛丽

题目描述:

假定有n个城堡,编号为1至n,有的城堡之间有道路直接相连,有的城堡之间没有道路直接相连。马里奥现在准备从一个城堡出发前往另一个城堡,它有一个魔法棒,可以瞬时通过一条道路,即以0时间通过这条道路,但魔法棒最多只能用一次。马里奥想以最短的时间到达目的地,请编写程序为马里奥选定一条路线以及在什么地方使用魔法棒。假定所有道路为双向,保证从起点肯定能到达目的地。
 
 

输入格式:

输入第一行为4个整数n、s、t、m,分别表示城堡数(编号为1至n,n不超过10000),马里奥所在的起点s和想去的终点t,城堡间的道路数目。接下来m行,每行为3个正整数a、b、c,表示城堡a和城堡b之间有一条道路直接相连,通过该道路需要c分钟。
 

输出格式:

输出为空格间隔的2个整数,第1个整数为马里奥从起点到目的地所需的最短时间;第2个整数表示使用魔法棒的地点,即城堡编号,若有多个可能的地点,则输出编号最小者。
 

输入样例:

4 1 4 4
1 2 9
2 4 1
1 3 3
3 4 5

输出样例:

1 1

代码实现:

#include<iostream>
#include<queue>
#include<algorithm> 
#include<cstring>
#include<vector>
using namespace std;
typedef pair<int,int>PII;
const int N=2e4+5,INF=0x3f3f3f3f;
//h数组存储邻接表的头,ne数组存储下一个结点,e数组用于存储当前结点下标,w数组用于存储当前边的长度 
int h[N],ne[2*N],e[2*N],w[2*N],idx;
//dist1数组起点到任意其它点的最短距离 
//dist2数组终点到任意其它点的最短距离 
int dist1[N],dist2[N],vis[N];
//邻接表建边操作 
void add(int x,int y,int z){
	e[idx]=y,w[idx]=z,ne[idx]=h[x],h[x]=idx++;
}
void dijkstra(int x,int dist[]){
	//初始化所有结点都未被访问 
	memset(vis,0,sizeof vis);
	//初始化x到其他所有点的距离为最大值 
	memset(dist,0x3f,sizeof 4*N);
	//优先队列优化 
	priority_queue<PII,vector<PII>,greater<PII> >q;
	//第一个参数记录当前点到x点的距离
	//第二个参数记录当前点的下标 
	q.push({0,x});
	//将x到它自己的距离初始化为0 
	dist[x]=0;
	while(q.size()){
		//取出当前结点到x点的距离
		int dis=q.top().first;
		//取出当前点的下标 
		int s=q.top().second;
		//出队列 
		q.pop();
		//如果当前点已经访问过了,直接返回,相当于剪枝 
		if(vis[s])continue;
		//将当前结点标记为已访问 
		vis[s]=1;
		//遍历与当前结点s直接相连的所有结点 
		for(int i=h[s];~i;i=ne[i]){
			//取出直接相连的结点的下标 
			int j=e[i];
			//如果存在比x到j还短的路径 
			if(dist[j]>dis+w[i]){
				//更新x到j的最短路径 
				dist[j]=dis+w[i];
				//走当前点
				//即将当前点和当前点到x的最短距离入队列 
				q.push({dist[j],j});
			}
		}
	}
}
int main(){
	//邻接表头初始化 
	memset(h,-1,sizeof h);
	int n,s,t,m;
	//n表示城堡的个数,s表示起点,t表示终点,m表示道路数量 
	cin>>n>>s>>t>>m;
	while(m--){
		int x,y,z;
		cin>>x>>y>>z;
		//建立双向边 
		add(x,y,z);
		add(y,x,z);
	}
	//先跑一遍起点到任意其它点的最短距离 
	dijkstra(s,dist1);
	//再跑一遍终点到任意其它点的最短距离 
	dijkstra(t,dist2);
	//res用于记录起点到终点且使用一次魔法棒的最短路径
	//idx1用于记录使用魔法棒所在的最小城堡编号 
	int res=INF,idx1=-1;
	//遍历起点的中间结束点 
	for(int i=1;i<=n;i++){
		//遍历与i结点直接相连的结点 
		for(int j=h[i];~j;j=ne[j]){
			//k表示与结点i直接相连的结点编号 
			int k=e[j];
			//dist1[i]相当与起点到结点i的最短路径
			//diat2[k]相当于终点到结点k的最短路径
			//而i和k是直接相连的,所以dist1[i]+dist2[k]相当与起点到终点的距离减去中间i结点到k结点的距离
			if(dist1[i]+dist2[k]<res){
				//如果存在起点到终点的更小值,则更新起点到终点的最短距离
				res=dist1[i]+dist2[k];
				//同时更新使用魔法棒所在的最小城堡编号(切记要保证取i和k两者最小值) 
				idx1=min(i,k);
			}else if(dist1[i]+dist2[k]==res){
				//如果存在另一条最短距离相同的路径,则只需要更新使用魔法棒所在的最小城堡编号 
				idx1=min(idx1,min(i,k));
			}
		}
	}
	//输出最短距离和使用魔法棒所在的最小城堡编号  
	cout<<res<<" "<<idx1<<endl;
	return 0;
}

 

posted @ 2023-04-21 11:57  回忆、少年  阅读(76)  评论(0编辑  收藏  举报