Dijkstra算法
用于计算有权图中一个节点到其他节点的最短路径。
基本原理
从起始点出发,重复寻找所有点中距离源点最近的且未访问过的结点,把该节点设置为已访问,然后利用该结点更新该节点的邻居节点的距离数组,直到访问过全部结点为止,最终的距离数组即为源点到其余各点的最短路径距离。
注:
距离数组:用于记录当前所有点到源点的距离,D[i]表示从源点到第i个点的最短路径距离。初始时数组中的每个值应该为无穷大。这个数组在算法进行中被不断更新,Dijkstra算法结束后,此数组中各个值应该表示源点到其余各点的最短路径距离。
已访问的点:每一次重复中找到的、**距离源点最近(D[i]最小)**且未访问过的节点。
算法正确性
如图,0是源点。与0相连的三个点中,0-2的距离是最短的,那么从0到2的最短路径长就是2,即D[2]=2
。原因是从0出发,若走中途不经过2的路径到达2,那么一定会经过1或3,从0到1或从0到3的距离就已经比从0直接到2的距离长了。若中途经过2最后又回到2,那么就绕了圈,也是比从0直接到2长的。所以D[2]=2
。然后把2设置为已访问,代表已经确定了从源点出发到2的最短路径长,后续不应该再更新D[2]
了(体现了dp的思想)。
2是已访问的点,1和3是他的邻居节点。根据算法,还要把点1和点3距离源点的距离D[1]
和D[3]
进行更新。虽然不能确定此时的D[1]
和D[3]
就一定是从源点到点1和点3的最短路径,但是对于这样的点,可以一边遍历所有节点一边不断更新D[i]
为更小的值,来找到可能的最短路径。
接下来,按照距离数组中的数据,找到与源点距离最短的点,标记为已访问。目前,距离数组中最小值为D[1]=3
,根据算法,可以确定从0到1的最小距离就是3。
我们这样做是否正确,就决定了这个算法是否正确。一般地,设集合S表示已访问点的集合,(对于本例,S的初始成员显然地就是0/0和2)对于每一个已访问点i,其到源点的最短距离已然确定,那么D[i]+点i到其邻居节点的距离
就是从源点出发,经过节点i,到达这个邻居节点的最短路径。在算法进行的过程中,在S集合到达这个状态之前,我们已经遍历过所有已访问点,并且按照上述方法更新了所有他们的邻居节点k的D[k]最小值。那么此时所有不在S中的点k,且不为无穷大的D[k]值就表示从源点出发,只能经过集合S中的点,最终到达该点k的最小距离。每一个D[k]值都对应着唯一的一条这样的线路。
现在假设有一条比这条线路还短的线路,起点终点不变。那么他就无法“只能经过集合S中的点”了,设路径中第一个不在S中的点为w。那么“起点—w”长度<“起点—w—k”长度<D[k]。而w是第一个不在S中的点,那么在S中必然能找到一个点,使w是他的邻居节点。说明对于所有不在S中的点,且不为无穷大的D值,是D[w]而不是D[k],这就与前提条件矛盾。因此,说明D中的最小值D[k]对应的线路,就是从源点出发到节点k的最小距离。
走山路与heap优化
题目描述
某同学在一处山地里,地面起伏很大,他想从一个地方走到另一个地方,并且希望能尽量走平路。 现有一个m*n的地形图,图上是数字代表该位置的高度,"#"代表该位置不可以经过。 该同学每一次只能向上下左右移动,每次移动消耗的体力为移动前后该同学所处高度的差的绝对值。现在给出该同学出发的地点和目的地,需要你求出他最少要消耗多少体力。
输入
第一行是整数 m,n,p,m是行数,n是列数,p是测试数据组数。 0 <= m,n,p <= 100 接下来m行是地形图 再接下来n行每行前两个数是出发点坐标(前面是行,后面是列),后面两个数是目的地坐标(前面是行,后面是列)(出发点、目的地可以是任何地方,出发点和目的地如果有一个或两个在"#"处,则将被认为是无法达到目的地)
输出
n行,每一行为对应的所需最小体力,若无法达到,则输出"NO"
样例输入
4 5 3
0 0 0 0 0
0 1 1 2 3
# 1 0 0 0
0 # 0 0 0
0 0 3 4
1 0 1 4
3 4 3 0
样例输出
2
3
NO
Dijkstra做法
m,n,case=map(int,input().split())#行数、列数、测试用例
output=[]#输出
mount_map=[]#地形图,内涵有权图的信息
for _ in range(m):
mount_map.append(input().split())#str型
direction=[(-1,0),(1,0),(0,1),(0,-1)