1. 程式人生 > >UVA 1599 Ideal Path(雙向bfs+字典序+非簡單圖的最短路+隊列判重)

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

ems code can scan min 時機 define index end

https://vjudge.net/problem/UVA-1599

給一個n個點m條邊(2<=n<=100000,1<=m<=200000)的無向圖,每條邊上都塗有一種顏色。求從結點1到結點n的一條路徑,使得經過的邊數盡量少,在此前提下,經過邊的顏色序列的字典序最小。一對結點可能有多條邊,一條邊可能連接相同的結點(自環)。輸入保證結點1可以到達結點n。顏色是1~10^9的整數。

分析:

  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 <iostream>
      2 #include <algorithm>
      3 #include <string>
      4 #include <sstream>
      5 #include <set>
      6 #include <vector>
      7 #include <stack>
      8 #include <map>
      9
    #include <queue> 10 #include <deque> 11 #include <cstdlib> 12 #include <cstdio> 13 #include <cstring> 14 #include <cmath> 15 #include <ctime> 16 #include <functional> 17 using namespace std; 18 19 #define maxn 100000 20 #define inf 0x7fffffff 21 22 typedef struct ver 23 { 24 int num, color; //邊的另一端的結點編號 和 顏色 25 ver(int n, int c) : num(n), color(c) {} 26 } Ver; 27 28 int n, m, a, b, c; 29 int d[maxn], res[maxn]; //d記錄每個點到終點的最短距離 res記錄最短路的顏色 30 bool vis[maxn], inqueue[maxn]; //vis每個結點是否被訪問過 inqueue標記結點是否加入了隊列,防止重復加入 31 vector<Ver> edge[maxn]; //鄰接表記錄圖 32 33 void bfs(int start, int end) 34 { 35 memset(inqueue, 0, n); 36 memset(vis, 0, n); 37 int u, v, c; 38 queue<int> q; 39 q.push(start); 40 if (start == 0) //用於正向BFS 41 { 42 memset(res, 0, sizeof(int) * n); 43 while (!q.empty()) 44 { 45 u = q.front(); 46 q.pop(); 47 vis[u] = 1; 48 if (u == n - 1) 49 return; 50 int minc = inf, len = edge[u].size(); 51 for (int i = 0; i < len; i++) 52 if (!vis[v = edge[u][i].num] && d[u] - 1 == d[v]) 53 minc = min(edge[u][i].color, minc); //獲取所有路徑中最小的顏色 54 for (int i = 0; i < len; i++) 55 if (!vis[v = edge[u][i].num] && d[u] - 1 == d[v] && edge[u][i].color == minc && !inqueue[v]) 56 q.push(v), inqueue[v] = 1; //若有多組顏色相同且未入隊,則將其入隊 57 int index = d[0] - d[u]; //獲得當前步數對應的下標 58 if (res[index] == 0) 59 res[index] = minc; 60 else 61 res[index] = min(res[index], minc); //獲取最小顏色 62 } 63 } 64 else 65 while (!q.empty()) //用於反向DFS 構建層次圖,找最短路 66 { 67 u = q.front(); 68 q.pop(); 69 vis[u] = 1; 70 for (int i = 0, len = edge[u].size(); i < len; i++) 71 if (!vis[v = edge[u][i].num] && !inqueue[v]) 72 { 73 d[v] = d[u] + 1; //一定是頭一次入隊,這通過inqueue保證 74 if (v == 0) 75 return; //找到起點退出 76 q.push(v); //如果不是起點,就把這個點入隊 77 inqueue[v] = 1; //入隊標記 78 } 79 } 80 } 81 82 int main() 83 { 84 while (scanf("%d%d", &n, &m) == 2) 85 { 86 for (int i = 0; i < n; i++) 87 edge[i].clear(); 88 memset(d, -1, sizeof(int) * n); 89 d[n - 1] = 0; //初始化的細節 90 while (m--) 91 { 92 scanf("%d%d%d", &a, &b, &c); 93 if (a != b) //排除自環 94 { 95 edge[a - 1].push_back(ver(b - 1, c)); 96 edge[b - 1].push_back(ver(a - 1, c)); 97 } 98 } 99 bfs(n - 1, 0); //先反向BFS 100 bfs(0, n - 1); //再正向BFS 101 printf("%d\n%d", d[0], res[0]); 102 for (int i = 1; i < d[0]; i++) 103 printf(" %d", res[i]); 104 printf("\n"); 105 } 106 return 0; 107 }

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