今天为大家介绍一下Dijkstra算法及其模板,并且解决蓝桥杯省赛出现的真题——路径 Dijkstra算法
⭐Dijkstra算法的基本思想是:每次找到里源点最近的一个顶点,然后以该顶点为中心进行扩展,最终得到雨点到其余所有点的最短路径。(dist[i]数组代表从源点到顶点i的最短路,st[i]用来标记该点是否已经被更新)
⭐基本步骤如下:
⭐1.将所有的顶点分为两部分:已知最短路程的顶点集合P和未知最短路径的顶点集合Q。最开始,已知最短路径的顶点集合P中只有源点一个顶点。我们这里用一个bool类型数组st[]来记录哪些点在集合P中。例如对于某个顶点i,如果st[i]为true则表示这个顶点在集合P中,如果st[i]为false则表示这个顶点在集合Q中。
⭐2.设置源点s到自己的最短路径为0即dist[1]=0, 若存在有源点能直接到达的项点1。则把dist[i]设为e[s][i]同时把所有其他(源点不能直接到达的)顶点的最短路径设为∞。
⭐3.在集合Q中的所有顶点中选择一个离源点s最近的顶点u(即dist[u]最小) 加入到集合P。并考察所有以点u为起点的边,对每一条边进行松弛操作。例如存在从u到v的一条边有dist[v]>dist[u]+e[u][v](即从源点直接到v的距离大于从源点先到u再从u到v的距离),我们可以用新值dist[u]+e[u][v]来替代当前dist[v]中的值。
⭐4.重复第3步,如果集合Q为空,算法结束。最终dist数组中的值就是源点到所有顶点的最短路径。
代码模板如下:
#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
int g[N][N];//用来存边
int dist[N];//记录每个点到源点的距离
bool st[N];//标记该点是否已更新
int n,m;
int pirm()
{
memset(dist,0x3f,sizeof dist);
dist[1]=0;
for(int i=0;i<n;i++)
{
int t=-1;
for(int j=1;j<=n;j++)
if(!st[i]&&(t==-1||dist[t]>dist[j]))
t=j;
st[t]=true;
for(int j=1;j<=n;j++)
dist[j]=min(dist[j],dist[t]+g[t][j]);
}
return dist[n];
}
int main()
{
scanf("%d%d",&n,&m);
memset(g,0x3f,sizeof g);
for(int i=1;i<=m;i++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
g[a][b]=min(g[a][b],c);
}
int t=dijkstra();
printf("%d\n",t);
}
下面是2021年蓝桥杯省赛真题就是用到了Dijkstra算法,这题写起来非常简单就是一道模板题,只不过中间还穿插了一些最大公约数和最小公倍数的知识,题目和Ac代码如下,这题就留给读者你们自己思考啦!!!
⭐
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll e[2050][2050],dist[2050];
bool st[2050];
ll gcd(ll a,ll b)//求公约数
{
return a%b==0?b:gcd(b,a%b);
}
ll lcm(ll a,ll b)//求最小公倍数
{
return a*b/(gcd(a,b));
}
int dijkstra()//Dijkstra算法
{
memset(dist,0x3f,sizeof dist);
dist[1]=0;
for(int i=1;i<=2021;i++)
{
int t=-1;
if(!st[i]&&(t==-1||dist[t]>dist[i]))
t=i;
st[t]=true;
for(int j=1;j<=2021;j++)
dist[j]=min(dist[j],dist[t]+e[t][j]);
}
return dist[2021];
}
int main()
{
memset(e,0x3f,sizeof e);
for(int i=1;i<=2021;i++)
{
for(int j=i;j<=2021;j++)
{
int k=j-i;
if(k>21)break;
else
{
e[i][j]=e[j][i]=lcm(i,j);
}
}
}
int ans=dijkstra();
cout<<ans<<endl;
}