最短路模板题,如果对最短路不是很熟悉的同学请移步:
传送门
进入正题
此题与其他题不同的是,每新增条边,就必须存储,然后等1操作到达时跑最短路,于是我们有dijkstra和SPFA两种跑最短路的方法:
其次,如何处理无法到达呢。只需要if(dis[]==inf)就行了。
因为我们dis一开始初始化为inf,如果在接下来的跑最短路中没有被更新(也就是dis==inf),说明由当前起点是到不了这个点的。
dinjkstra:
dijkstra的核心就在于它的算法步骤:
算法步骤: 1. 找离起点x最近的未讨论过的点k。 2. 判断经过k点,起点x到其他点的距离是否缩短,如缩短 则更新。将k点标记为已讨论。 3. 重复进行第1步,直到所有点都被讨论过为止。
所以dijkstra的实质是贪心,但是在上述步骤1中,我们可以使用强大的stl中的优先队列来快速查找。
所以dijkstra的时间复杂度为
:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=10005;
const ll inf=1e15;
struct node{ 重载运算符,将dis关键字从小到大排序
int num;
ll dis;
bool operator < (const node &a) const{
return dis>a.dis;
}
};
int n,m;
ll dis[N];
bool mark[N];
int Last[N],End[N],len[N],Next[N],tot;
void cb(int x,int y,int z){
End[++tot]=y;
len[tot]=z;
Next[tot]=Last[x];
Last[x]=tot;
}
void dijkstra(int s){
for(int i=1;i<=n;i++){
dis[i]=inf;
mark[i]=false;
}
dis[s]=0;
//mark[s]=true;
priority_queue<node> q; 以dis作为关键字的小根堆
q.push((node){s,0});
while(q.size()){
int u=q.top().num; 取出距离起点最近的点
q.pop();
if(mark[u])
continue;
mark[u]=true;
for(int i=Last[u];i;i=Next[i]){ 链式前向星
int v=End[i];
if(!mark[v] && dis[v]>dis[u]+len[i]){ 更新最短距离
dis[v]=dis[u]+len[i];
q.push((node){v,dis[v]});
}
}
}
}
int main(){
scanf("%d %d",&n,&m);
for(int i=1;i<=m;i++){
int opt,x,y,z;
scanf("%d",&opt);
if(opt){ opt==1 记录边
scanf("%d %d %d",&x,&y,&z);
cb(x,y,z);
cb(y,x,z);
}
else{
scanf("%d %d",&x,&y);
dijkstra(x); 跑最短路
if(dis[y]!=inf) 判断是否可以到达
printf("%lld\n",dis[y]);
else
puts("-1");
}
}
return 0;
}SPFA(bellman-ford)
算法流程:
用一个队列来进行维护。初始时将起点加入队列。
每次从队列中取出一个元素,并对他所连接的点进行松弛,若某个点松弛成功(则通过那个点到起点距离缩短),则将其入队。直到队列为空
简单的说就是队列优化的bellman-ford,利用了每个点不会更新次数太多的特点
SPFA的时间复杂度是O(kE),k一般取2左右(k是增长很快的 函数ackermann的反函数,2^65536次方也就5以下 ),可以处理负边。
:(其实跟dijkstra差不多)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=10005;
const ll inf=1e15;
int n,m;
ll dis[N];
bool mark[N];
int Last[N],End[N],len[N],Next[N],tot;
void cb(int x,int y,int z){
End[++tot]=y;
len[tot]=z;
Next[tot]=Last[x];
Last[x]=tot;
}
void spfa(int s){
for(int i=1;i<=n;i++){
dis[i]=inf;
mark[i]=false;
}
dis[s]=0;
mark[s]=true;
queue<int> q;
q.push(s);
while(q.size()){
int u=q.front();
q.pop();
mark[u]=false;
for(int i=Last[u];i;i=Next[i]){
int v=End[i];
if(dis[v]>dis[u]+len[i]){
dis[v]=dis[u]+len[i];
if(!mark[v]){
q.push(v);
mark[v]=true;
}
}
}
}
}
int main(){
scanf("%d %d",&n,&m);
for(int i=1;i<=m;i++){
int opt,x,y,z;
scanf("%d",&opt);
if(opt){
scanf("%d %d %d",&x,&y,&z);
cb(x,y,z);
cb(y,x,z);
}
else{
scanf("%d %d",&x,&y);
spfa(x);
if(dis[y]!=inf)
printf("%lld\n",dis[y]);
else
puts("-1");
}
}
return 0;
}SPFA判断负环可以看文章上方传送门
其实这道题还能用floyd
是不是很惊讶,floyd的时间复杂度怎么可能过的了!!?
但是我们可以边记录边进行更新,这样这道题的时间复杂度就是了!!!
题解区里有大佬讲的很清楚了,这里我就不细讲了。。。
总结:
这道题其实还是一道很不错的题,可以为巩固最短路来用!

京公网安备 11010502036488号