spfa算法,他死了。
为了纪念他,我们学习他的深度优先实现极其优化。
(下文建议一行行的去阅读,在阅读文章时请注意思考)
spfa算法,由于采用广搜的思想,中断了迭代的连续性(不丝滑)。而若直接采用深搜思想,找到一个节点,直接沿该节点向下扩展,会收获怎样的效果呢?
void spfa(node){
book[node]=true;//当前节点已在路径当中
for(v=node的所有出边的终点){
if(dis[node]+该出边权值<dis[v]){
dis[v]=dis[node]+该边权值;
if(!book[v]) spfa(v);
else return;//存在负权回路
}
}
book[node]=false;//完成一次回溯
}
两种算法原理相同,都保留了有用状态。
我们发现,在节点拓扑关系较强时,广搜会占很大优势,Dfs往往因为盲目迭代而浪费大量时间。
So,如何进行优化成了关键问题(关键问题在于问题的关键)
请读者进行思考再往下阅读。
---------------------------------------------------------------------------------------------------------------------------------
1.dfs确实暂时无法取得好的效果,但dfs仍然有极大优化的可能。
在dfs相关优化中,我们知道有可行性剪枝,最优性剪枝,记忆化,排除等效冗余。在这里,我们先运用“优化搜索顺序”的方式,这是计划的一部分。
将边按照一定规则排序,将权值按优劣排序,这样可以得到一个优秀的初始解。
我们来考虑使用贪心来进行优化。
bool spfa_intitialize(node){
for(v=node所有出边终点){
if(dis[node]+node到v边的边权<dis[v]){
松弛该边;
spfa_initialize(v);
return true;
}
return false;
}
void spfa(node){
book[node]=true;//当前节点已在路径当中
for(v=node的所有出边的终点){
if(dis[node]+该出边权值<dis[v]){
dis[v]=dis[node]+该边权值;
if(!book[v]) spfa(v);
else return;//存在负权回路
}
}
book[node]=false;//完成一次回溯
}
signed main(){
for(node=1~v)
while(spfa_initialize(node));
for(node=1~v)
spfa(node);
}
这就是对于每个节点,只更新一条边然后就退出。
比较bfs&dfs的区别,发现bfs可以通过队列后,每个点有可能再次进入队列寻找更加棒的路径。(好棒好棒)
BUT!!!dfs的哒咩之处就在于---------它会让一个点进行一种很深特别深超级深深得离谱的迭代
这令我们开始思考:能不能通过别的方式,来进行对他的优化呢?
能不能让他的迭代层数更less呢?
(请读者思考再阅读下文,2~5 min).
---------------------------------------------------------------------------------------------------------------------------------
经过思考,各自应该有了各种神奇的答案。若有与下文不同,请发布在评论区。( =))
---------------------------------------------------------------------------------------------------------------------------------
最eeeeeeeeasy的方式就是:控制递归层数。
想必这也是大多数人会第一个想到的--------控制了递归层数,不就可以解决问题了吗?
(addd)
废话区域(请跳过阅读):
相信大家在学习深搜与广搜时,老师总会跟你讲起“迭代深搜”这个东西。
而现在,这个东西派上用场了!(不了解迭代深搜可以去搜索)
以前你是不是还以为“迭代深搜不就是广搜的深搜版本吗”
现在他就用于spfa的优化了!
(Bi~~~~~----~~~~~----)
加入这个优化后,我们发现:在bfs极其不擅长的网格型数据中,dfs在用时上仅为bfs的三分之一。
加入这些简单的优化后,时间可能还是有点小小的难以接受。但是不要灰心,dfs极其灵活的算法框架在真实应用上可以进行不同的优化。如查找正负环等。在题目中应适时运用不同de优化,dfs不会让你失望呢,(p.s.:dfs本身就有lots of优化,这里就不一一赘述了)