上次讲了图的储存,现在,我们就正式开始单源最短路的学习吧。
7 11 5 4
2 4 2
1 4 3
7 2 2
3 4 3
5 7 5
7 3 3
6 1 1
6 3 4
2 4 3
5 6 3
7 2 1
还是来看这个图,目前我们已知的是这个图的各个边的情况以及它们的值,那么,如何求最短路呢?
Bellman-Ford算法通过从起点开始不断更新所有路径的最短距离,来实现最短路的求解。
下面看示例图解释(这里我们用一个数组d来存最短路径)(为了方便理解,我决定编个小故事):
很久很久以前,一个二次元世界上有七个大陆,每个大陆都不知道其他大陆的存在。
后来,一个从未来穿越过来的人来到了5号大陆,告诉那里的最有智慧的人其他六个大陆的存在。
然后使用了魔法,在七个大陆中随即建造了11条路。
这个未来人告诉五号大陆中最有智慧的人说:
“我想要前往四号大陆,你能帮我找到前往四号大陆的最短路径吗?我将非常感谢你。”
“实践是检验真理的唯一标准,所以,我应该去实践!”于是,五号大陆的智者出发了,并制作了图表来方便自己记录,毕竟,好记性不如烂笔头。
d: 1 2 3 4 5 6 7
B B B B 0 B B
这里的B为INF(INF我们设置为一个很大的数,例如0x3f3f3f),因为智者并不知道五号大陆和其他大陆之间的距离,不过看上去很远,因为他只能看到海。
由于我们要求5到4的最短路,所以,这里5为起点,自己到自己的最短距离是0。于是智者将5的位置标为0。
接下来,未来人依靠超能力(别问为什么有超能力)告诉智者他建造的路的标号,但是没有告诉他路的长度。
于是,开始从上往下看有关5号大陆和其他大陆联通的情况。
5 7
很快,智者找到了这个,于是他决定先测量5号大陆和7号大陆的距离。
最后,结果为5。
于是,智者在自己制作的图表上记下了自己充满智慧的一笔。
d 1 2 3 4 5 6 7
B B B B 0 B 5
此时,按理来说,智者应该开始看和7有关的联通大陆,但是未来人觉得这样太慢了,隔空传音告诉他,他将拥有分身,每个大陆都会有一个分身。
所以,现在,只要和5与7有关的都可以去测量了(由于分身来源于本体,所以,一次要么本体移动,要么其中一个分身移动。
比如,接下来智者很快就看到了
7 3
于是,不用我说,智者测量为3,接下来,图表为这样。
d 1 2 3 4 5 6 7
B B 8 B 0 B 5
由于未来人需要的是5到4的距离,所以这里3为5到7到3,而不是7到3的距离,所以为8。
最后,经过智者不懈的努力,图表变成了这个样子:
d 1 2 3 4 5 6 7
4 6 7 7 0 3 5
至此,智者很满意和骄傲自豪的对未来人说:“是7!”
未来人一脸欣慰的对智者竖起大拇指,说道:“很好!”
随后,智者的图表传给后代,最后来到了Bellman-Ford的手中(bushi
下面上代码:
const int INF= 0x3f3f3f;//设置一个INF为很大的值
int d[6210];//d是最短路
void short_path(int k)
{
for(int i=1;i<=n;i++)d[i]=INF;//设置起点到每个点的距离为B,很大
d[k]=0;//起点到起点距离为0
while(true){
bool update=false;//如果不再更新,就说明所以的路况已经探查清楚,并且都已经是最短路了。
for(int i=1;i<=m;i++)
{
edge e=es[i];
if(d[e.from ]!=INF&&d[e.to ]>d[e.from]+e.cost)//如果起点到终点的距离小于原先记录的,则更新值。
{
d[e.to]=d[e.from]+e.cost;//更新原先记录的。
update=true;//更新,说明又可能还要继续更新。
}
if(d[e.to]!=INF&&d[e.from ]>d[e.to]+e.cost)//这里不是单向图,所以终点到起点的距离也要更新记录。
{
d[e.from]=d[e.to]+e.cost;
update=true;
}
}
if(!update)break;//如果不再更新就结束循环。
}
}
判断负环的代码这里先不介绍了,等以后再做介绍OvO
下面让我们打上这个,去LuoguAC题目吧!(P1339)
short_path(s);
cout<<d[t];