在学图论的时候,我们就会遇到最短路问题。如果数据量不大的话就可以用Floyd算法,时间复杂度为。
但当数据量为大约500以上时,用Floyd就很有可能会超时。这时候,我们就需要用到dijkstra算法。
算法讲解
dijkstra算法的基本思想就是:先把所有点变成“白点”,每一步再选择当前点所连接的其他白点权值最小的点,标记为“蓝点”,然后对其他连接的点进行松弛操作,这样,我们最后得到的就是起点到每个其他点的最短距离。
例如下面是一个图
要求1到其他点的最短路
我们先找到1连接的3和5结点,更新它们的距离。
其中5最小,于是把它标记为蓝点。
我们再进行松弛操作,更新他周围的点的最短距离。
同样的,我们可以得到说有点的最短路径。
例题
题目:热浪
题目描述
德克萨斯纯朴的民众们这个夏天正在遭受巨大的热浪!他们的德克萨斯长角牛吃起来不错,可是他们并不是很擅长生产富含奶油的乳制品。Farmer John此时以先天下之忧而忧,后天下之乐而乐的精神,身先士卒地承担起向德克萨斯运送大量营养冰凉的牛奶的重任,以减轻德克萨斯人忍受酷暑的痛苦。
Farmer John已经研究过可以把牛奶从威斯康星运送到德克萨斯州的路线。这些路线包括起始点和终点先一共经过 T (1 < = T < = 2500)个城镇,方便地标号为 1 到 T 。除了起点和终点外的每个城镇由两条双向道路连向至少两个其他城镇。每条道路有一个通过费用(包括油费、过路费等)。
给定一个地图,包含 C (1 < = C < = 6200)条直接连接 2 个城镇的道路。每条道路由道路的起点 Rs 、终点 Re (1 < = Rs < = T ,1 < = Re < = T)和花费(1 < = Ci < = 1000)组成。求从起始的城镇 Ts(1 < = Ts < = T)到重点的城镇 Te(1 < = Te < = T)最小的总费用。
输入输出格式
输入格式:
第1行:4个有空格隔开的整数:T,C,Ts,Te。
第2到第 C+1 行:第 i+1 行描述第 i 条道路。有 3 个由空格隔开的整数:Rs,Re,Ci。
输出格式:
一个单独的整数表示从 Ts 到 Te 的最小总费用。数据保证至少存在一条道路。
输入输出样例
输入样例#1:
7 11 5 4
2 4 2
1 4 3
7 2 2
3 4 3
5 7 5
7 3 3
6 1 1
6 3 4
2 4 3
5 6 3
7 2 1
输出样例#1:
7
提示信息
样例说明:
5 - > 6 - > 1 - > 4 (3 + 1 + 3)
这道题就完全是个模板题,数据范围不大,但用Floyd会超时。这时候就适合用dijkstra算法。
#include<bits/stdc++.h>
using namespace std;
const int BIG=99999999;
int a[2510][2510]; //地图
int d[2510]; //起点到第i个点的最短距离
int n,m,A,B; //A:起点 B:终点
bool b[2510]; //记录是白点还是蓝点
void dij(){
for(int i=1;i<=n;i++) if(A!=i) d[i]=BIG; //将d数组初始化为无限大
for(int i=1;i<=n;i++){
int _min=BIG,m;
for(int j=1;j<=n;j++)
if(_min>d[j]&&!b[j]){
_min=d[j];
m=j;
} //找到边权最小的白点
b[m]=1; //标记为蓝点
for(int j=1;j<=n;j++){
if(a[m][j]!=0&&!b[j]&&d[m]+a[m][j]<d[j])
d[j]=d[m]+a[m][j]; //松弛操作
}
}
}
int main(){
scanf("%d%d%d%d",&n,&m,&A,&B);
for(int i=1,j,k,l;i<=m;i++){
scanf("%d%d%d",&j,&k,&l);
a[j][k]=a[k][j]=l;
} //输入
dij(); //dijkstra算法
printf("%d\n",d[B]);
}
有时候,我们会要求输出路径。要输出路径的话,只需在建一个数组pre来记录前驱就好了。
可以用递归输出。
#include<bits/stdc++.h>
using namespace std;
const int BIG=99999999;
int a[2510][2510]; //地图
int d[2510]; //起点到第i个点的最短距离
int n,m,A,B; //A:起点 B:终点
int pre[2510]; //记录路径
bool b[2510]; //记录是白点还是蓝点
void out(int p){
if(pre[p]!=0) out(pre[p]);
printf("%d ",p);
}
void dij(){
for(int i=1;i<=n;i++) if(A!=i) d[i]=BIG; //将d数组初始化为无限大
for(int i=1;i<=n;i++){
int _min=BIG,m;
for(int j=1;j<=n;j++)
if(_min>d[j]&&!b[j]){
_min=d[j];
m=j;
} //找到边权最小的白点
b[m]=1; //标记为蓝点
for(int j=1;j<=n;j++){
if(a[m][j]!=0&&!b[j]&&d[m]+a[m][j]<d[j]){
d[j]=d[m]+a[m][j]; //松弛操作
pre[j]=m; //记录下路径
}
}
}
}
int main(){
scanf("%d%d%d%d",&n,&m,&A,&B);
for(int i=1,j,k,l;i<=m;i++){
scanf("%d%d%d",&j,&k,&l);
a[j][k]=a[k][j]=l;
} //输入
dij(); //dijkstra算法
printf("%d\n",d[B]);
out(B);
}