Bellman_Ford是通过N-1次循环求出每个点到原点的最短路的,每次循环遍历所有的边,如果能更新就更新。前面说过为什么N-1次就行(若一个点到起点的最短路需要经过N个点,包括他自己,那么第N次循环就能确定他的最短路)。
最短路一定是不含环的,如果存在负环就根本不存在最短路了。所以这个方法可以用来判断是否有负环,如果循环了N-1次后还能进行松弛操作,说明有负环。Bellman_Ford的复杂度是O(M*N)。
int bellman(){ //G条边
int i,j,k;
memset(d,INF,sizeof(d));
d[0]=0;
for(i=0;i<N-1;i++){
for(j=0;j<G;j++){
int u=e[j].u,v=e[j].v;
if(d[u]!=INF&&d[u]+w[u][v]<d[v]) d[v]=d[u]+w[u][v];
}
}
for(j=0;j<G;j++){
int u=e[j].u,v=e[j].v;
if(d[u]!=INF&&d[u]+w[u][v]<d[v]) return 1;
}
return 0;
}
因为再次循环的时候有些点的d根本没有被更新,再用这个点去更新别的点的话就浪费时间了。对Bellman_Ford改进,Spfa算法是用一个队列,每次取出一个点,更新和他连接的点,如果被更新的点不在队列中就加入队列中,这样就避免了那个问题。因为是对每个点连接的点操作,这就需要链接表了(可以用vector数组)。还需要一个数组inq判断当前这个点是否在队列里。
如果用spfa判断是否有负环问题,其实原理也是一个点不能被更新N-1次以上,所以再开一个数组记录每个点被加入队列的次数。在网上看说是一个点被加入N次以上就说明有负环,想了半天为什么是N次以上。。。后来发现因为这是有特殊情况的,N=1,这个点被加入一次,但是没负环。。。所以只要判断这个特殊情况,后面条件写成N-1次以上也是对的。。当然为了方便,直接写成N次以上更好。。网上说这个期望时间复杂度O(2*e),这个我就不知道怎么算的了。。。
int spfa(){
queue<int> q;
memset(d,INF,sizeof(d));
memset(inq,0,sizeof(inq));
memset(cnt,0,sizeof(cnt));
d[0]=0;
inq[0]=1;
q.push(0);
while(!q.empty()){
int x=q.front();
q.pop();
inq[x]=0;
cnt[x]++;
if(cnt[x]>N) return 1;
if(d[x]==INF) continue;
for(int i=0;i<v[x].size();i++){
int y=v[x][i].y;
if(d[x]+v[x][i].t<d[y]){
d[y]=d[x]+v[x][i].t;
inq[y]=1;
q.push(y);
}
}
}
return 0;
}