1. 程式人生 > >【最短路徑】 SPFA算法優化

【最短路徑】 SPFA算法優化

png mage 是什麽 a算法 分類 什麽 ref spfa算法 判斷語句

  首先先明確一個問題,SPFA是什麽?(不會看什麽看,一邊學去,傳送門),SPFA是bellman-ford的隊列優化版本,只有在國內才流行SPFA這個名字,大多數人就只知道SPFA就是一個頂尖的高效算法,卻不知道還能繼續優化,這個優化雖然也沒有你想的那麽麻煩,只不過多了幾個判斷語句罷了,5分鐘就能學會,但是這也得運用到分類討論,其實SPFA有三種優化方法,效果並不是很明顯。

技術分享圖片

  這三個測試點通過情況所對應的分別是SPFA的三種優化方法,這個時間也是因題而異,像這道題,效果並不好,但是看別人寫的博客,他們提交了一道數據對於優化後的SPFA比較有利,測試時間差距能看出來,但是效果也就是減少十幾毫秒而已,但是也是有的,萬一題目會卡這十幾毫秒呢?

1. SLF優化

  還記得嗎?在我們學SPFA的時候,要把每一個入隊的點插入到隊尾,可是有些時候這個點作為隊尾沒有作為隊頭效率高,因為這個點有時放在隊首就能直接用,那麽什麽樣的點作為隊首更好呢?當然是dis值越小越可能刷新其它dis值,所以對比當前元素與對首元素的dis值,如果當前元素的dis值更小,那麽把當前元素插入到隊首,否則插入到隊尾。如果你不是很了解隊列,或者還是現學的,一定會納悶,不就能q.push( );嗎?哪來的隊首呢?此時queue<int>q;應該改為deque<int>q;雙端隊列,就有q.push_front( );和q.push_back( );了。

代碼如下(紅色處為優化對應的新增代碼):

 1 void SPFA()
 2 {
 3     memset(dis,inf,sizeof(dis));
 4     deque<int>q;
 5     q.push_back(1);dis[1]=0;vis[1]=1;
 6     while(q.size())
 7     {
 8         x=q.front();q.pop_front();vis[x]=0;
 9         for(int i=head[x];i;i=map[i].next)
10         {
11             s=map[i].to;
12 if(dis[s]>dis[x]+map[i].value) 13 { 14 dis[s]=dis[x]+map[i].value; 15 if(vis[s]==0) 16 { 17 if(dis[s]<dis[q.front()]) q.push_front(s); 18 else q.push_back(s); 19 vis[s]=1; 20 } 21 } 22 } 23 } 24 }

2. LLL優化

  如果懂了上一個SLF優化,那麽這個LLL優化就很好理解了,SLF表示小的優先,LLL表示大的最後,那麽什麽樣的的dis值是大的呢?難道還和隊首元素比較嗎?當然不是,是於隊列的平均數來比較,如果大於這個平均數就放到最後。

代碼如下(紅色處為優化對應的新增代碼):

 1 void SPFA()
 2 {
 3     memset(dis,inf,sizeof(dis));
 4     queue<int>q;
 5     q.push(1);dis[1]=0;vis[1]=1;
 6     while(q.size())
 7     {
 8         p=q.front();q.pop();
 9         if(dis[p]*cnt_2>sum)
10         {
11             q.push(p);
12             continue;
13         }
14         sum-=dis[p];cnt_2--;
15         vis[p]=0;
16         for(int i=head[p];i;i=map[i].next)
17         {
18             s=map[i].to;
19             if(dis[s]>dis[p]+map[i].value)
20             {
21                 dis[s]=dis[p]+map[i].value;
22                 if(vis[s]==0)
23                 {
24                     vis[s]==1;
25                     q.push(s); 
26                     cnt_2++;
27                     sum+=dis[s];
28                 }
29             }
30         }
31     }
32 }

2. SLF+LLL優化

  這個就很簡單直接了,把兩個新增代碼搓一塊了就行。

代碼如下(紅色處為優化對應的新增代碼):

 1 void SPFA()
 2 {
 3     memset(dis,inf,sizeof(dis));
 4     deque<int>q;
 5     q.push_back(1);dis[1]=0;vis[1]=1;
 6     while(q.size())
 7     {
 8         p=q.front();q.pop_front();
 9         if(dis[p]*cnt_2>sum)
10         {
11             q.push_back(p);
12             continue;
13         }
14         sum-=dis[p];cnt_2--;
15         vis[p]=0;;
16         for(int i=head[p];i;i=map[i].next)
17         {
18             s=map[i].to;
19             if(dis[s]>dis[p]+map[i].value)
20             {
21                 dis[s]=dis[p]+map[i].value;
22                 if(vis[s]==0)
23                 {
24                     vis[s]==1;
25                     if(dis[s]<dis[q.front()]) q.push_front(s);
26                     else q.push_back(s);
27                     cnt_2++;
28                     sum+=dis[s];
29                 }
30             }
31         }
32     }
33 }

  怎麽樣,你學會了嗎?

【最短路徑】 SPFA算法優化