題目連接:http://acm.hdu.edu.cn/showproblem.php?pid=5876php
題意:ios
有一個 n 個點無向圖,再給你 m 對頂點, 表明着這 m 對頂點之間沒有邊, 除此以外每兩個點之間都有一條邊, 且權值爲 1.而後還有一個源點 S, 讓你計算源點到其餘各點之間的最短距離,若是不存在則輸出 -1.也就是說讓你在所給的圖的補圖上求源點到其餘各點的最短路徑.算法
思路:數組
補圖上求最短路徑算是比較經典的題.在這裏所求的最短路其實並不須要用到 dijkstra 之類的算法,因爲每條邊之間的距離都爲 1,每條邊的權值同樣.那麼就能夠想到這個作法:優化
步驟1:根據題意建圖.而後創建一個隊列,把源點 S 壓入隊列,其餘的各個點存到另外一個集合 V 裏, 並創建一個數組來存儲源點到其餘各點的最短距離,初始化爲 -1, dis[S] = 0.spa
步驟2:從隊列首部取出一個節點 v,訪問全部與節點 v 不相鄰的節點 u,把 u 壓入到隊列尾部, 並從集合 V 中把 u 除去, 更新dis[u] = dis[v] + 1.code
步驟3:反覆重複步驟 2, 直到隊列爲空.以後 dis 數組裏保存的就是答案.blog
解決了怎麼作以後,尚未完,因爲題目給的點數很是大,因此還須要優化下時間.這裏比較費時間的就是步驟 2 中的找不相鄰的點.這裏就能夠用 STL 中的 set 來維護集合 V, 也就是未被訪問到的點. 初始化 set1 中爲全部點(除去源點 S), 當訪問的點 v 時, 在 set1 中除去與點 v 相鄰的點 u, 並加入到 set2 中,那麼 set1 中剩下的點就是全部與點 v 不相鄰的點, 依次遍歷壓入隊列. 以後再把 set2 拷貝到 set1, 那麼 set1 就是剩下的未被訪問到的點,如此反覆下去.能夠看到,對於每條邊只訪問一次.每一個點也只進一次隊列.因此總的時間複雜度爲 O(n * m),能夠達到要求了.隊列
notes:創建 無向圖 的時候每條邊要存兩次. 因此數組的大小必定是原題所給的邊數的兩倍! 兩倍! 兩倍!順便求一個用鏈表實現的代碼,本身用鏈表沒寫出來.(我好菜啊.jpgget
代碼:
1 #include <iostream> 2 #include <cstdio> 3 #include <queue> 4 #include <set> 5 #include <cstring> 6 #include <algorithm> 7 8 using namespace std; 9 typedef long long LL; 10 11 const int MAXN = 200000; 12 const int MAXE = 20000; 13 int n, m, T, S; 14 int dis[MAXN + 3];//保存最終的最短距離 15 16 int head[MAXN + 3], len; //鏈式前向星 17 struct NODE {int to; int next; }; 18 NODE edge[2 * MAXE + 3]; 19 20 void addedge(int u, int v) { //鏈式前向星加邊 21 edge[len].to = v; 22 edge[len].next = head[u]; 23 head[u] = len++; 24 } 25 26 void BFS() { //從起點開始BFS 27 memset(dis, -1, sizeof(dis)); 28 queue <int> Qu; 29 Qu.push(S); dis[S] = 0; //起點初始化 30 set<int> unsed, hep; //用來維護還沒有被訪問的點 31 for(int i = 1; i <= n; i++) unsed.insert(i); 32 unsed.erase(S); 33 while( !Qu.empty() ) { 34 int tp = Qu.front(); Qu.pop(); 35 for(int k = head[tp]; k != -1; k = edge[k].next) { //從 unsed 中除去和當前拓展節點相鄰的點,同時加入到臨時輔助的集合中 36 if(unsed.find(edge[k].to) != unsed.end()) { 37 unsed.erase(edge[k].to); 38 hep.insert(edge[k].to); 39 } 40 } 41 for(set<int>::iterator it = unsed.begin(); it != unsed.end(); it++) {// unsed 暫時保存的是和當前拓展節點不相鄰的點 42 Qu.push(*it); 43 dis[*it] = dis[tp] + 1; 44 } 45 hep.swap(unsed), hep.clear();//從輔助集合中 copy 剩下的未被訪問到的點. 46 } 47 } 48 49 int main() { 50 scanf("%d", &T); 51 while(T--) { 52 memset(head, -1, sizeof(head)); 53 scanf("%d%d", &n, &m); 54 int u, v; len = 0; 55 for(int i = 0, len = 0; i < m; i++) { 56 scanf("%d%d", &u, &v); 57 addedge(u, v); addedge(v, u); 58 } 59 scanf("%d", &S); 60 BFS(); 61 for(int i = 1, j = 0; i <= n; i++) if(i != S) printf("%d%c", dis[i], " \n"[++j == n - 1]); 62 } 63 return 0; 64 }