带负权的单源最短路径

     昨天比赛的时候碰到了带负权的单源最短路径的题目,没做出来,Dijkstra不可以求带负权的最短路。学习了一下Bellman-ford算法。

 

Bellman-ford算法可以在O(VE) 的时间内求出带负权的单源最短路(除存在负权回路),而且可以判断图中是否存在负权回路。

我觉的Bellman-ford算法的关键点在于在一个|v |个点的图中,任意两个点之间的最短路之间最多有|v|-1条边,这样把每条边松弛|v|-1次后就是其最短距离,该算法就是这样不断松弛逐渐逼近最短距离。

判断是否存在带负权的回路:

因为每条边经过|v|-1次松弛后就是其最短距离了,如果说还有边可以松弛,那就该图中一定存在带负权的回路。否则不存在。

 

int Bellman_ford()
{
       for(int i=0;i<n;i++)    // 初始化,距离赋值为无穷大,Dist[i]表示i点到源点最短距离
               Dist[i]=Inf;
       Dist[start]=0;
       for(int i=0;i<n-1;i++)     //对每条边松弛|v-1|次,这里采用边集数组的存储方式
       {
               for(int k=0;k<cunt;k++)
               {
                       if(Dist[E[k].v]>Dist[E[k].u]+E[k].cost)   // 松弛判断
                         Dist[E[k].v]=Dist[E[k].u]+E[k].cost;
                                 
               }
       }
       for(int i=0;i<cunt;i++)
       {
               if(Dist[E[i].v]>Dist[E[i].u]+E[i].cost)       //判断是否存在负权回路,flag为0表示存在负权回路
               {
                         flag=0;                           
                         break;
               }         
       }

}                     
                    

 

 

Bellman-ford算法的改进优化—SPFA算法:

Bellman-ford可以利用队列进行优化,也就是SPFA算法:

 

SPFA对Bellman-Ford算法优化的关键之处在于意识到:只有那些在前一遍松弛中改变了距离估计值的点,才可能引起他们的邻接点的距离估计值的改变。因此,算法大致流程是用一个队列来进行维护,即用一个先进先出的队列来存放被成功松弛的顶点。初始时,源点s入队。当队列不为空时,取出队首顶点,对它的邻接点进行松弛。如果某个邻接点松弛成功,且该邻接点不在队列中,则将其入队。经过有限次的松弛操作后,队列将为空,算法结束。SPFA算法的实现,需要用到一个先进先出的队列 queue 和一个指示顶点是否在队列中的标记数组InQ。为了方便查找某个顶点的邻接点,图采用临界表存储。在SPFA中判断存在负权回路的方法是如果一个点进队次数大于|v|次,则表示存在负权回路,所以用一个数组f表示进对次数。

代码:

void Spfa()
{
       
        queue<int > q;
        q.push(start);  //源点进队
        for(int i=0;i<n;i++) // 初始化
        {      
                InQ[i]=false;
                f[i]=0;
                Dist[i]=Inf;
        }    
        Dist[start]=0;flag=1;
        while(!q.empty())
        {
         
            int temp=q.front();
            q.pop();
            InQ[temp]=false;
            if(f[temp]>n)
            {
                 flag=0;
                 break;
            }        
            for(int i=0;i<adj[temp].size();i++)
            {
                int v=adj[temp][i];
                if(Dist[v]>Dist[temp]+cost[temp][v])   //松弛
                {       
                    if(!InQ[v])
                    {  
                        q.push(v);
                        InQ[v]=true;
                        f[v]++;
                    }    
                    Dist[v]=Dist[temp]+cost[temp][v];
                }         
                                
            }
        }                   
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值