Dijsktra的算法理解—朴素算法(n2+M)
**请注意:下面的理解方式及其残忍(doge),Dijkstra算法我尽量会用通俗的故事去解释帮助理解,之后会更新堆优化版dijsktra(mlogn)。如有雷同,纯属巧合。**
一、题目分析:
给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环,所有边权均为正值。
请你求出 1 号点到 n 号点的最短距离,如果无法从 1 号点走到 n 号点,则输出 −1。
二、思路分析:
求1到n点的最短距离,可能会出现重边和自环,不会出现负权的边,且为有向无环图。(自环后面再
说)所以是单源最短路问题,用dijkstra算法。
开始讲故事:
在一个蚂蚁家族里面,食物已经所剩无几,所以一只蚂蚁姑且叫他工具蚁1号想出去找食物,而这个蚁穴呢就是n=1的点,还好这个蚂蚁家族和狗呢关系非常不错,工具蚁叫狗来帮忙探路,但是外面呢是从没被探过的路,(玩过红警星际的可以理解为战争迷雾只有去过才会有那个地方的视野)所以工具蚁1让狗先跑向蚁穴能直接通向的点,如图(左),小狗先跑到2号点回来告诉他蚁穴到2号点的距离为1,然后又让小狗跑到3号点得知距离为4,(有没有发现放狗的范围很像bfs)那么工具蚁1当然走向距离最短的2号点。然后在2号点带着狗探3号点,发现距离为2,1+2<4,那么他推出1号点先到2号点再去3号点距离最短。由此可知,先走的最近的2号点是可以更新之前走过的地方的最短距离的。
再来看,图(右),同样是走过2号点和三号点,但2号点的蚂蚁发现走到3号点的距离为5,1+5>4,工具蚁1就知道自己走了歪路,只有每次走最短路才能活着回去,加上自己饥肠辘辘没办法全身而返,就让狗子回去工具蚁2:“吾命休矣,工具蚁2,照顾好我老婆和孩子。”工具蚁1顿了顿又说:“来的时候别走2号线。”就这样工具蚁1倒在了路上,狗子舔了舔倒下的工具蚁1,跑回去告诉工具蚁2……
分析故事里面的条件:
首先题目会给出:1、边与边的权值——g[][],但这是游戏自动生成的,蚂蚁(玩家)现在还不知道。
2、蚂蚁知道的最短距离只有蚁穴,他不知道其他的点离自己有多远,所以有个dis[j]数组(用来记录从1号点到j的最短距离),将1以外的所有点初始化为0x3f3f3f3f(按字节赋值,即正无穷)
3、因为狗子一次只能探一个方向的一个地点就会回来,那么当蚂蚁探完n-1个点的时候下一次一定会探到第n个点即终点,所以可以只用n-1个工具蚁就可以完成使命,那么循环次数只用n-1次。或者说,从蚁穴到终点其间(1到n-1)的点都能更新2到n的dis[]
4、前一个工具蚁所走过的路的信息将会传递给下一位,所以st[]数组来标记之前所有的工具蚁去试过错的地方,下一个蚂蚁就没必要来尝试
接下来看代码
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N =503;
int g[N][N]; //在稠密图里面(点的数量比较多,空间复杂度需求不用很大)用邻接矩阵比较直观
int n,m; //定义点数和变数
bool st[N];
int dis[N];
int dijkstra() // 求1号点到n号点的最短路距离,如果从1号点无法走到n号点则返回-1
{
memset(dis,0x3f,sizeof dis); //涉及memset()的赋值操作,将dis初始化为正无穷(一个很大很大的值)
dis[1]=0; //蚁穴
for(int i=0;i<n;i++) //进行n-1次壁外探索
{
int t=-1; //这里为什么要初始化为-1 之后马上会看到,t设置是为了记录蚂蚁走向最近的路所到达的点是哪个
for(int j=1;j<=n;j++) //看看n个点有没有可以走的路
if(!st[j]&&(t==-1||dis[t]>dis[j])) //!st[j]为没有试过错的点,就去试试。但是蚂蚁在蚁穴的时候不用试错dis[1]==0,所以对于未知的正无穷(战争迷雾),得让狗子去探路
t=j; //找到了最近的路,那就走呗
st[t]=true; //这个路试过错了,下个蚂蚁就不用来试了
for(int j=1;j<=n;j++) //看看有什么点到1的最短路(dis[])是可以更新的,比如上面的3点就是 由2点的蚂蚁计算1+2=3<4得到的
dis[j]=min(dis[j],dis[t]+g[t][j]);
}
if(dis[n]==0x3f3f3f3f)return -1; //如果找完了,蚂蚁都上完了,还没有更新dis[n](还没找到食物的话),那么到n的路不存在,蚂蚁biss
return dis[n];//否则返回最短路
}
int main()
{
cin>>n>>m;
memset(g,0x3f,sizeof g);
for(int i=1;i<=m;i++){
int a,b,c;
cin>>a>>b>>c;
g[a][b]=min(g[a][b],c);
}
int t=dijkstra();
cout<<t<<endl;
}