UVa1599 Ideal Path(雙向bfs+字典序+非簡單圖的最短路+隊列判重)


題目大意:

對於一個n個房間m條路徑的迷宮(Labyrinth)(2<=n<=100000, 1<=m<=200000),每條路徑上都塗有顏色,顏色取值范圍為1<=c<=10^9。求從節點1到節點n的一條路徑,使得經過的邊盡量少,在這樣的前提下,如果有多條路徑邊數均為最小,則顏色的字典序最小的路徑獲勝。一條路徑可能連接兩個相同的房間,一對房間之間可能有多條路徑。輸入保證可以從節點1到達節點n。

 更多細節可以參考原題:UVa1599

分析:

  1. 從題目中我們可以看出,題目中的無向圖是可以出現自環和重邊的,自環我們可以在輸入的時候檢查並排除,但是重邊我們需要保留,並從中選擇顏色最小的邊。
  2. 題目的數據量很大,不可能采用鄰接矩陣存儲圖,因此應采用鄰接表,且鄰接表便於進行bfs
  3. 路徑的顏色不代表路徑的權重,本題中路徑是無權的  

 

思路:

從終點開始倒着bfs一次,得到每個點到終點的距離,然后從起點開始,按照每次距離減1的方法尋找接下來的點的編號。按照顏色最小的走,如果有多個顏色最小,則都拉入隊列中,將最小的顏色記錄在res數組中。

其中,index=d[0]-d[u]就得到了當前u節點對應的距離,也就是步驟數

 

細節:

  1. 已經進入隊列的節點不能重復入隊,否則復雜度太高,會tle(重復入隊的復雜度至少是O(n^2),在n=100000的情況下直接tle)
  2. 第一次bfs和第二次bfs的終止時機不同,第一次找到起點就終止,第二次則是從隊列中取出節點時才能終止,為的是遍歷完所有導向終點且路徑長度一致的邊,只有這樣才能結果正確
  3. d數組記錄每個節點到終點n的距離,不能用0進行初始化,而終點處的初始化必須是0
  4. d數組不能不初始化,否則對於多輸入題目,前面的輸入可能影響后面的輸出

代碼實現如下:

 1 #include<cstdio>
 2 #include<vector>
 3 #include<queue>
 4 #include<cstring> 
 5 using namespace std; //min()函數 
 6 #define max 100000
 7 #define inf 0x7fffffff
 8 typedef struct ver{
 9     int num, color;//邊的另一端的節點編號 和 顏色 
10     ver(int n,int c):num(n),color(c){}
11 }Ver;
12 int n,m,a,b,c;
13 int d[max],res[max];//d記錄每個點到終點的最短距離 res記錄最短路的顏色
14 bool vis[max],inqueue[max];//vis每個節點是否被訪問過 inqueue標記節點是否加入了隊列,防止重復加入 
15 vector<Ver> edge[max];//鄰接表記錄圖 
16 void bfs(int start,int end){
17     memset(inqueue,0,n);
18     memset(vis,0,n);
19     int u,v,c;
20     queue<int> q;
21     q.push(start);
22     if(start==0){//用於正向bfs 
23         memset(res,0,sizeof(int)*n);
24         while(!q.empty()){
25             u=q.front();q.pop();vis[u]=1;
26             if(u==n-1)return;
27             int minc=inf,len=edge[u].size();
28             for(int i=0;i<len;i++)if(!vis[v=edge[u][i].num] && d[u]-1==d[v])minc=min(edge[u][i].color,minc);//獲取所有路徑中最小的顏色
29             for(int i=0;i<len;i++)if(!vis[v=edge[u][i].num] && d[u]-1==d[v] && edge[u][i].color==minc && !inqueue[v])q.push(v),inqueue[v]=1; //若有多組顏色相同,且未入隊,則將其入隊 
30             int index=d[0]-d[u];//獲得當前步數對應的下標
31             if(res[index]==0)res[index]=minc;
32             else res[index]=min(res[index],minc);//獲取最小顏色 
33         }
34     }//用於反向bfs 構建層次圖,找最短路 
35     else while(!q.empty()){
36         u=q.front();q.pop();vis[u]=1;
37         for(int i=0,len=edge[u].size();i<len;i++)if(!vis[v=edge[u][i].num] && !inqueue[v]){ 
38             d[v]=d[u]+1; //一定是頭一次入隊,這通過inqueue保證 
39             if(v==0)return; //找到起點,退出 
40             q.push(v);//如果不是起點,就把這個點入隊 
41             inqueue[v]=1;//入隊標記 
42         }
43     }
44 }
45 int main(){
46     while(scanf("%d%d",&n,&m)==2){
47         for(int i=0;i<n;i++)edge[i].clear();
48         memset(d,-1,sizeof(int)*n);d[n-1]=0;//注意初始化的細節 49         while(m--){
50             scanf("%d%d%d",&a,&b,&c);
51             if(a!=b){ //排除自環
52                 edge[a-1].push_back(ver(b-1,c));
53                 edge[b-1].push_back(ver(a-1,c));
54             }
55         }
56         bfs(n-1,0);//反向bfs
57         bfs(0,n-1);//正向bfs 
58         printf("%d\n%d",d[0],res[0]);
59         for(int i=1;i<d[0];i++)printf(" %d",res[i]);
60         printf("\n");
61     }
62 }

 

 

收獲:

這是第一次學習bfs遍歷復雜圖,對於重邊和自環的處理也終於有了一點經驗,積累了自己的bfs最短路的模板

另外,UVa上的數據並不是完全可靠,對於用0初始化數組d的行為,可以用這組數據測試出wa的結果:

Input:

4 3

1 2 1

1 3 1

1 4 7

Output:

1

7

但是我實驗發現,如果用0對數組d進行初始化,在UVa上仍能AC,不過我已經給UVa寫信報告了這個bug,不知道他們會不會做修正。

不論如何,這道題還是收獲很大滴~接下來是

 

反向bfs尋找最短路的模板

注意:d數組初始化應該用-1,並將d[n-1]=0,否則就會出現上述UVa的bug

這份代碼假設在輸入的時候重邊已經被排除否則這份代碼還需要加入u!=v的判斷

代碼如下:

 1 void rbfs(){
 2     memset(inqueue,0,sizeof(inqueue));
 3     memset(vis,0,sizeof(vis));
 4     queue<int> q;q.push(n-1);
 5     while(!q.empty()){
 6         u=q.front();q.pop();vis[u]=1;
 7         for(int i=0,len=edge[u].size();i<len;i++)if(!vis[v=edge[u][i].num] && !inqueue[v]){ //inqueue是為了防止重復入隊造成復雜度過高,以本題為例,如果允許重復入隊會直接超時 
 8             d[v]=d[u]+1;
 9             if(v==0)return; //找到起點,退出 
10             q.push(v);//如果不是起點,就把這個點入隊 
11             inqueue[v]=1;//入隊標記 
12         }
13     }
14 }

 

今天就到這里啦~再見呦(●'◡'●)~~撒花撒花*★,°*:.☆\( ̄▽ ̄)/$:*.°★*

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM