最短路基础算法一览
一、朴素版Dijkstra
849. Dijkstra求最短路 I
dijkstra算法的基本原理是遍历寻找离起点距离最短且没有被访问过的点,标记此点,将其并入起点。此处为最简单的Dijkstra实现模版。
ac代码实现
#define N 505
int mp[N][N];
bool vis[N];
int dis[N];
int n,m;
int dijkstra()
{
//memset(dis,0x3f,sizeof dis);
dis[1]=0;
vis[1]=true;
for(int i=0;i<n-1;i++){
int t=-1;
for(int j=1;j<=n;j++){
if(!vis[j]&&(t==-1||dis[j]<dis[t])){
t=j;
}
}
vis[t]=1;
for(int j=1;j<=n;j++){
dis[j]=min(dis[j],dis[t]+mp[t][j]);
}
}
if(dis[n]>=INF){
return -1;
}
else
return dis[n];
}
int main()
{
scanf("%d%d",&n,&m);
memset(mp,0x3f,sizeof mp);
for(int i=0;i<m;i++){
int s,e,l;
scanf("%d%d%d",&s,&e,&l);
mp[s][e]=min(mp[s][e],l);
}
for(int i=1;i<=n;i++){
dis[i]=mp[1][i];
}
cout<<dijkstra()<<endl;
}
例题1、
畅通工程续
int vis[210];
int dis[210];
int main()
{
int n,m;
int mp[210][210];
while(~scanf("%d%d",&n,&m)){
for(int i=0;i<n;i++){
vis[i]=0;
dis[i]=INF;
}
//地图预处理
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(i==j)
mp[i][j]=0;
else
mp[i][j]=INF;
}
}
for(int i=0;i<m;i++){
int x,y,d;
cin>>x>>y>>d;
if(mp[x][y]>d){
mp[x][y]=d;
mp[y][x]=d;
}
}
int start,end;
cin>>start>>end;
//初始化dis数组
for(int i=0;i<n;i++){
dis[i]=mp[start][i];
}
vis[start]=1;
//合并n-1个点
for(int i=1;i<=n-1;i++){
int min=INF;
for(int j=0;j<n;j++){
if(dis[j]<min&&vis[j]!=1){
min=dis[j];
}
}
for(int j=0;j<n;j++){
if(dis[j]==min){
vis[j]=1;
for(int k=0;k<n;k++){
if(dis[k]>dis[j]+mp[j][k]){
dis[k]=dis[j]+mp[j][k];
}
}
}
}
}
if(dis[end]!=INF)
cout<<dis[end]<<endl;
else
cout<<-1<<endl;
}
}
二、优先队列优化版Dijkstra
即利用优先队列来优化查询每一次的最小距离。
王国朝贡
以下可以作为模版,该模版包括了邻接表的建立以及Dijkstra优先队列优化。
#define INF 0x3f3f3f3f
#define ll long long
const int maxn=1e5+7;
struct edge{
int to,cost;
};
typedef pair<int,int>P;
vector<edge> mp1[maxn];//邻接表
vector<edge> mp2[maxn];
int dis[maxn];
int N,M;//点的个数和边数
void add1(int a,int b,int l)
{
int inx=a;
edge e;
e.to=b;
e.cost=l;
mp1[inx].push_back(e);
}
void add2(int a,int b,int l)
{
int inx=b;
edge e;
e.to=a;
e.cost=l;
mp2[inx].push_back(e);
}
void dijkstra1()
{
memset(dis,INF,sizeof dis);
priority_queue<P,vector<P>,greater<P>> que;
dis[1]=0;
que.push(P(0,1));
while(que.size()){
P now=que.top();
que.pop();
int v=now.second;
if(dis[v]<now.first) continue;
for(int i=0;i<mp1[v].size();i++){
edge e=mp1[v][i];
if(dis[e.to]>dis[v]+e.cost){
dis[e.to]=dis[v]+e.cost;
que.push(P(dis[e.to],e.to));
}
}
}
}
void dijkstra2()
{
memset(dis,INF,sizeof dis);
priority_queue<P,vector<P>,greater<P>> que;
dis[1]=0;
que.push(P(0,1));
while(que.size()){
P now=que.top();
que.pop();
int v=now.second;
if(dis[v]<now.first) continue;
for(int i=0;i<mp2[v].size();i++){
edge e=mp2[v][i];
if(dis[e.to]>dis[v]+e.cost){
dis[e.to]=dis[v]+e.cost;
que.push(P(dis[e.to],e.to));
}
}
}
}
int main()
{
scanf("%d%d",&N,&M);
for(int i=1;i<=M;i++){
int a,b,l;
scanf("%d%d%d",&a,&b,&l);
add1(a,b,l);
add2(a,b,l);
}
ll sum=0;
dijkstra1();
for(int i=2;i<=N;i++){
//cout<<i<<" dis[i]="<<dis[i]<<endl;
if(dis[i]<INF){
sum+=dis[i];
}
else{
printf("-1\n");
return 0;
}
}
dijkstra2();
for(int i=2;i<=N;i++){
if(dis[i]<INF){
sum+=dis[i];
}
else{
printf("-1\n");
return 0;
}
}
printf("%lld\n",sum);
}
三、Bellman-Ford
遍历路径并松弛
有边数限制的最短路
ac代码
struct edge{
int x,y;
int z;
}ed[10010];
ll dis[510],backup[510];//用ll,防止负边权更新INF
int n,m,k;
ll bellman_ford()
{
ll ans;
dis[1]=0;
for(int i=0;i<k;i++){
memcpy(backup,dis,sizeof dis);//因为限制边数,防止串联松弛,只能使用上一次循环得到的结果来松弛
for(int j=1;j<=m;j++){
dis[ed[j].y]=min(dis[ed[j].y],backup[ed[j].x]+ed[j].z);
//用backup数组更新,保证一次循环对于到一个点的路径只增加一条边
}
}
ans=dis[n];
return ans;
}
int main()
{
memset(dis,0x3f,sizeof dis);
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=m;i++){
cin>>ed[i].x>>ed[i].y>>ed[i].z;
}
ll ans=bellman_ford();
if(ans>=INF){
cout<<"impossible"<<endl;
}
else
cout<<ans<<endl;
}
四、SPFA
spfa本质是对bellman_ford的优化,因为bellman_ford每次都遍历所有边,但实际上有些边根本用不上,spfa优化就解决了这个问题。
基本模版:
spfa求最短路
#define ll long long
using namespace std;
const int maxn=1e5+7;
struct ed{
int to;
ll w;
};
vector<ed> mp[maxn];
int n,m;
ll dis[maxn];
bool vis[maxn];
void spfa(){
memset(dis,0x3f,sizeof dis);
memset(vis,0,sizeof vis);
dis[1]=0;
vis[1]=1;
queue<int> q;
q.push(1);
while(q.size()){
int a=q.front();
q.pop();
vis[a]=0;
for(int i=0;i<mp[a].size();i++){
ed e=mp[a][i];
if(dis[e.to]>dis[a]+e.w){
dis[e.to]=dis[a]+e.w;
if(!vis[e.to]){
vis[e.to]=1;
q.push(e.to);
}
}
}
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for(int i=1;i<=m;i++){
int a,b,c;
cin>>a>>b>>c;
mp[a].push_back({b,c});
}
spfa();
if(dis[n]<0x3f3f3f3f3f3f3f3f)
cout<<dis[n]<<endl;
else
cout<<"impossible"<<endl;
}
五、Floyd
极少用到,这个太慢了qaq,而且空间复杂度也高。
//用邻接矩阵存图
for(k=1;k<=n;k++)
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if(e[i][j]>e[i][k]+e[k][j])
e[i][j]=e[i][k]+e[k][j];