前序:
之前自己写过朴素版本的迪杰斯特拉,这里再补充一个优化版本的。
题目:
(双向图版本)
链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
LOST GRACE DISCOVERED
史东薇尔城坐落于宁姆格福与利耶尼亚湖的交界处,地势险要、易守难攻,是连接南北重要的交通枢纽。史东薇尔城不仅地理位置重要,内部更是错综复杂,第一次来到这里的褪色者往往会迷路。魔法师MaverickFW接手了大量的来自史东薇尔城的任务委托,每一次执行任务MaverickFW都需要从自己当前的所在地去往史东薇尔城再跑去任务地点。我们将地图简化为一张无向图,而史东薇尔城是一号结点。MaverickFW想知道自己每次任务最短需要跑多远,他每次会告诉你自己当前所在结点与任务地所在结点,希望你能准确回答他。
输入描述:
第一行两个正整数n,m表示结点数与边数 接下来m行,每行三个正整数vi,wi,di,表示vi到wi的距离为di 接下来一行一个整数T,表示询问次数 之后T行,每行两个正整数si,ti表示MaverickFW的出发点与任务点
输出描述:
对于每一个询问输出一行一个正整数表示最短距离
示例1
输入
3 3 1 2 1 1 3 1 2 3 1 3 1 3 2 3 1 2
输出
1 2 1
代码:
#include "iostream"
#include "queue"
#include "vector"
#include "cstring"
using namespace std;
const int N=1e6+9;
typedef long long LL;
typedef pair<int,int> PI;
int to[N],val[N],nex[N],head[N],n,m,cnt=0;
LL dis[N];
void add(int a,int b,int c){
to[++cnt]=b;
val[cnt]=c;
nex[cnt]=head[a];//nex是针对与点来说的,看这个点相连接的下一条边是什么
head[a]=cnt;
}
void DJS(){
bool vis[N];
priority_queue<PI,vector<PI>,greater<PI>> tool;
tool.push({0,1});
while(!tool.empty()){
PI temp=tool.top();
tool.pop();
int now=temp.second;
if(vis[now])//这里一定要注意加上是否访问过这个点了
continue;
vis[now]=true;
for(int i=head[now];i;i=nex[i]){//这里需要小心nex与to的区别
int j=to[i];
dis[j]=min(dis[j],dis[now]+val[i]);
tool.push({dis[j],j});
}
}
}
int main(){
cin>>n>>m;
for(int i=0;i<m;i++){
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);
add(b,a,c);//题目中说的是双向图,所以需要添加两次边
}
memset(dis,0x3f3f3f,sizeof(dis));
dis[1]=0;
DJS();
int times,x,y;
cin>>times;
while(times--){
cin>>x>>y;
cout<<dis[x]+dis[y]<<endl;
}
return 0;
}
主要依靠堆优化来完成,正常的迪杰斯特拉,可能1e3或者1e4就是极限了,但是优化版本的可以直接提升到1e6的数量级,因为时间复杂度为O(Nlog(N)),所以大大减小了。
简要的思想便是,通过邻接表存储边的信息!这个存储过程背下来就是了(如果实在记不清的话,然后多打几遍,就知道是什么意思了,to和nex数组是针对不同的东西的,to是指下一个点是什么,nex是说from点所连接的下一条边是什么,很重要!)然后使用priority_queue来记录顺序,每一次取顶上的元素就好了。
最后由于这个是双向图的版本,所以在add的时候,需要add(a,b,c)与add(b,a,c)两个情况都需要加上!
最后在循环所有的边和查看vis数组的时候,联想到之前朴素版本的情况就可以很简单的更新好整个过程!