最近做了一些题,感觉对算法更深刻的理解是比套板子更深层次的,在这个层次上解决问题,思路会更加清晰。比如P5687 [CSP-S2019 江西] 网格图(题解)这道题就是网格图的最小生成树,解法就建立在普通Kruskal的基础上,当时想了挺久也没想出来,看了题解才豁然开朗。所以各算法总是要回顾回顾的~
除了做题,没事想一想算法的工作原理,也会对算法有更深的理解。以后我会多写一些对一些算法的剖析和理解(可能并不是特别严谨,主要以可读性为主,以供随时回顾)。
Dijkstra的算法全过程
- 初始定义\(S=\varnothing,T=V\)。
- 重复下列操作,直到\(T=\varnothing\):
- 从\(T\)中找到到\(S\)距离最近的节点\(u\),此时\(u\)的最短路确定。
- 将\(u\)从\(T\)中取出,转入\(S\),并对\(u\)的邻接节点进行松弛操作。
Dijkstra正确性证明
对于节点\(u\),我们要证明它加入\(S\)时,我们确定下来的最短路是实际的最短路。
反证,假设在操作\(1\)结束后,有\(x\in T\),使得存在\(T\rightarrow\dots\rightarrow x\rightarrow u\),使得\(dis[x]+v<dis[u]\),由于边权\(v\)非负,所以一定有\(dis[x]<dis[u]\),那么\(x\)一定提前于\(u\)进入\(T\)并对\(u\)进行松弛了,与\(x\notin T\)矛盾。
同时这也解释了Dijkstra为什么不能处理负权边:如果存在负权边,我们就不能推出\(dis[x]<dis[u]\)。
以前有段时间疑惑过为什么不能让所有边都加某个值来去除负权边,然而这样很显然是错误的: