讲真,刚看到这个算法的时候我一脸懵逼,这是什么鬼,不过这也正常,大部分时候我看到一个新的算法总是一脸懵逼(笑),不过这没什么,
像三体里说的“弱小和无知不是生存的障碍,傲慢才是”,所以,好好学就ok啦(*^_^*)。
说了那么多废话,接下来进入正题啦!
所谓最短路问题是图论中最基础的问题,是给定两个点,在以这2个点为起点和终点的路径中找到最短的那条。
什么是最短呢?我们会有这个疑惑,是经过的顶点数或者边数最少?还是经过的边的权值和最小呢?
在这里指的是经过的边的权值和最小。而单源最短路径问题是固定一个顶点,求它到其他所有点的最短路的问题。
Bellman-Ford算法的基本思想是遍历所有的边,更新从起点到所有顶点的路径权值和。闲话少说,放码过来:
struct edge{
int from;
int to;
int cost;
};//这是边,from,to为某条边的起点与终点,cost为权值
int V,E;//V为图中顶点个数,E为图中的边数
int d[V];//记录最短路径
struct edge es[E]//存储边的数组
void shortest_path(int s){//s为出发的顶点
for(int i = 0;i<V;i++) d[i]=INF;//初始化顶点s到各个点的距离为无穷大,这样更新的时候就很方便了
d[s]=0;//起始点的路径长短为0
//接下来考虑怎么更新d[i]中的路径大小
while(1){
bool update = false;//如果在下面的for循环中有更新路径,用update来记录
for(int i = 0;i<E;i++){//遍历所有的边,因为我们求的最短路径关乎边,
以边来循环才能确保所有的路径都被检查过,不然可能找不到最短的路径
edge e = es[i];
if(d[e.from!=INF]&&d[e.to]>d[e.from]+e.cost){
//这个条件用来判断什么时候更新路径,首先要更新e.to顶点的路径,
那么它前面的顶点e.from必须被走过,然后更新后的路径确实比原来的短,
就进行更新
d[e.to]=d[e.from]+e.cost;
update = true;//在for循环中有过更新操作,则将update改为true。
}
}
if(!update) break;//这一步很关键,如果单单循环一次for的话顶点有的有被更新,
有的没有,所以要用while循环,但什么时候跳出while循环呢?
显然是当所有的路径都更新了的时候。但怎么判断所有的路径都被更新了呢?
很简单,当一次for循环没有任何一个路径被更新时。
}
return;
}
不知道你们注意到没有,假如,假如,这个图中有负圈的话,更新操作就不是有限的,就无法跳出while循环,所以,要先检查图中是否存在负圈。怎么检查呢?只要检查循环次数就可以了。为什么呢?因为在不存在负圈的情况下,最短路不会经过同一个顶点2次,也就是说,最多通过V-1条边,while循环最多执行V-1次,反之,如果存在负圈,那么第V次循环也会更新d的值。以此来检查是否有负圈。
bool find_negative_loop(){
memset(d,0,sizeof(int));//将d全部初始化为0;
for(int i=0;i<V;i++){
for(int j=0;j<E;j++){
edge e = es[i];
if(d[e.to] > d[e.from]+cost){
d[e.to] = d[e.from] + e.cost ;
if(i==V-1) return true;
}
}
}
return false;
}
好了,先这样吧。