六度空間
“六度空間”理論又稱作“六度分隔(Six Degrees of Separation)”理論。這個理論可以通俗地闡述為:“你和任何一個陌生人之間所間隔的人不會超過六個,也就是說,最多通過五個人你就能夠認識任何一個陌生人。”如圖1所示。
圖1 六度空間示意圖
“六度空間”理論雖然得到廣泛的認同,並且正在得到越來越多的應用。但是數十年來,試圖驗證這個理論始終是許多社會學家努力追求的目標。然而由於歷史的原因,這樣的研究具有太大的局限性和困難。隨着當代人的聯絡主要依賴於電話、短信、微信以及因特網上即時通信等工具,能夠體現社交網絡關系的一手數據已經逐漸使得“六度空間”理論的驗證成為可能。
假如給你一個社交網絡圖,請你對每個節點計算符合“六度空間”理論的結點占結點總數的百分比。
輸入格式:
輸入第1行給出兩個正整數,分別表示社交網絡圖的結點數N(1 < N ≤ 103 ,表示人數)、邊數M(≤ 33 × N,表示社交關系數)。隨后的M行對應M條邊,每行給出一對正整數,分別是該條邊直接連通的兩個結點的編號(節點從1到N編號)。
輸出格式:
對每個結點輸出與該結點距離不超過6的結點數占結點總數的百分比,精確到小數點后2位。每個結節點輸出一行,格式為“結點編號:(空格)百分比%”。
輸入樣例:
10 9 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10
輸出樣例:
1: 70.00% 2: 80.00% 3: 90.00% 4: 100.00% 5: 100.00% 6: 100.00% 7: 100.00% 8: 90.00% 9: 80.00% 10: 70.00%
解題思路
一開始以為這道題目很難,但想了一下找到了解題思路,發現其實還蠻簡單的。當時第一反應是用DFS,不過最后一個測試點沒過。后面在網上發現這道題不能夠用DFS,因為我們不是把整一個圖都遍歷一遍,而是只遍歷指定深度的點,這會導致一些在指定深度范圍內的點范圍不到,遍歷就結束了。下面來舉個例子:
但按照六度空間理論,這個例子中,按照路線1->3->5->6->7->8->9,頂點9也是可以訪問的。從這個例子可以看到DFS在本題中並不適用。如果要用DFS,就必須進行相應的修改。
所以后面我改用了BFS。和原本的BFS一樣,只不過我們需要添加一個遍歷深度不超過6的條件。不過有一個難點就是,怎么知道每層的最后一個點是哪個?因為我們只有找到每層的最后那個點,深度才可以+1。
當時我是這樣想的,設一個標識符“-1”,代表着如果從隊列中彈出“-1”就表明該層的頂點都已經訪問過了,深度需要+1,同時再把“-1”壓入隊列。
相應的代碼如下:
1 int BFS(LGraph *graph, int src) { 2 bool vis[graph->verN] = {false}; 3 int depth = 0, cnt = 1; // 變量depth用來記錄遍歷的深度,cnt用來記錄從始點src開始一共可以遍歷得到的點的個數 4 std::queue<int> q; 5 q.push(src); 6 vis[src] = true; 7 q.push(-1); // 在把src壓入隊列后,再壓入"-1",代表src是第0層的最后一個點 8 9 while (!q.empty() && depth < 6) { // 循環條件為隊列不為空並且深度不超過6 10 int v = q.front(); 11 q.pop(); 12 if (v == -1) { // 如果彈出的是"-1" 13 depth++; // 深度+1 14 q.push(-1); // 同時再把"-1"壓入隊列,在隊列中"-1"的前一個元素就是深度+1后那一層的最后一個點 15 continue; 16 } 17 18 for (AdjVNode *w = graph->G[v]; w; w = w->next) { 19 if (!vis[w->adjVer]) { 20 q.push(w->adjVer); 21 vis[w->adjVer] = true; 22 cnt++; // 每訪問一個新的點,cnt就要+1 23 } 24 } 25 } 26 27 return cnt; 28 }
姥姥還給了另外一種思路,就是用一個變量來記錄每層最后的那個點。每次在把從隊列中彈出的點的鄰接的點壓入隊列后,判斷從隊列中彈出的點是否與記錄的那個點相同,如果相同就深度+1,同時在把那個變量更新記錄當前隊列的最后一個元素,而這個元素就是該層的最后那個點。
相應的代碼如下:
1 int BFS(LGraph *graph, int src) { 2 bool vis[graph->verN] = {false}; 3 int depth = 0, last = src, cnt = 1; // last用來記錄某層的最后一個點,一開始第0層的最后一個點就是src 4 std::queue<int> q; 5 q.push(src); 6 vis[src] = true; 7 8 while (!q.empty() && depth < 6) { // 循環條件為隊列不為空並且深度不超過6 9 int v = q.front(); 10 q.pop(); 11 for (AdjVNode *w = graph->G[v]; w; w = w->next) { 12 if (!vis[w->adjVer]) { 13 q.push(w->adjVer); 14 vis[w->adjVer] = true; 15 cnt++; 16 } 17 } 18 if (v == last) { // 當發現某個點與last記錄的點相同,說明該層的點都已經變量過了 19 depth++; // 深度+1 20 last = q.back(); // 更新last,為隊列的最后一個元素,這個元素是該層的最后那個點 21 } 22 } 23 24 return cnt; 25 }
兩種版本的AC代碼如下:
1 #include <cstdio> 2 #include <queue> 3 4 struct AdjVNode { 5 int adjVer; 6 AdjVNode *next; 7 }; 8 9 struct LGraph { 10 AdjVNode **G; 11 int verN, edgeN; 12 }; 13 14 LGraph *createLGraph(int n, int m); 15 int BFS(LGraph *graph, int src); 16 17 int main() { 18 int n, m; 19 scanf("%d %d", &n, &m); 20 LGraph *graph = createLGraph(n, m); 21 22 for (int v = 0; v < graph->verN; v++) { 23 int cnt = BFS(graph, v); 24 printf("%d: %.2f%%\n", v + 1, 100.0 * cnt / graph->verN); 25 } 26 27 return 0; 28 } 29 30 LGraph *createLGraph(int n, int m) { 31 LGraph *graph = new LGraph; 32 graph->verN = n; 33 graph->edgeN = m; 34 graph->G = new AdjVNode*[graph->verN]; 35 for (int v = 0; v < graph->verN; v++) { 36 graph->G[v] = NULL; 37 } 38 39 for (int i = 0; i < graph->edgeN; i++) { 40 int v, w; 41 scanf("%d %d", &v, &w); 42 AdjVNode *tmp = new AdjVNode; 43 tmp->adjVer = w - 1; 44 tmp->next = graph->G[v - 1]; 45 graph->G[v - 1] = tmp; 46 47 tmp = new AdjVNode; 48 tmp->adjVer = v - 1; 49 tmp->next = graph->G[w - 1]; 50 graph->G[w - 1] = tmp; 51 } 52 53 return graph; 54 } 55 56 int BFS(LGraph *graph, int src) { 57 bool vis[graph->verN] = {false}; 58 int depth = 0, last = src, cnt = 1; 59 std::queue<int> q; 60 q.push(src); 61 vis[src] = true; 62 63 while (!q.empty() && depth < 6) { 64 int v = q.front(); 65 q.pop(); 66 for (AdjVNode *w = graph->G[v]; w; w = w->next) { 67 if (!vis[w->adjVer]) { 68 q.push(w->adjVer); 69 vis[w->adjVer] = true; 70 cnt++; 71 } 72 } 73 if (v == last) { 74 depth++; 75 last = q.back(); 76 } 77 } 78 79 return cnt; 80 }
1 #include <cstdio> 2 #include <queue> 3 4 struct AdjVNode { 5 int adjVer; 6 AdjVNode *next; 7 }; 8 9 struct LGraph { 10 AdjVNode **G; 11 int verN, edgeN; 12 }; 13 14 LGraph *createLGraph(int n, int m); 15 int BFS(LGraph *graph, int src); 16 17 int main() { 18 int n, m; 19 scanf("%d %d", &n, &m); 20 LGraph *graph = createLGraph(n, m); 21 22 for (int v = 0; v < graph->verN; v++) { 23 int cnt = BFS(graph, v); 24 printf("%d: %.2f%%\n", v + 1, 100.0 * cnt / graph->verN); 25 } 26 27 return 0; 28 } 29 30 LGraph *createLGraph(int n, int m) { 31 LGraph *graph = new LGraph; 32 graph->verN = n; 33 graph->edgeN = m; 34 graph->G = new AdjVNode*[graph->verN]; 35 for (int v = 0; v < graph->verN; v++) { 36 graph->G[v] = NULL; 37 } 38 39 for (int i = 0; i < graph->edgeN; i++) { 40 int v, w; 41 scanf("%d %d", &v, &w); 42 AdjVNode *tmp = new AdjVNode; 43 tmp->adjVer = w - 1; 44 tmp->next = graph->G[v - 1]; 45 graph->G[v - 1] = tmp; 46 47 tmp = new AdjVNode; 48 tmp->adjVer = v - 1; 49 tmp->next = graph->G[w - 1]; 50 graph->G[w - 1] = tmp; 51 } 52 53 return graph; 54 } 55 56 int BFS(LGraph *graph, int src) { 57 bool vis[graph->verN] = {false}; 58 int depth = 0, cnt = 1; 59 std::queue<int> q; 60 q.push(src); 61 vis[src] = true; 62 q.push(-1); 63 64 while (!q.empty() && depth < 6) { 65 int v = q.front(); 66 q.pop(); 67 if (v == -1) { 68 depth++; 69 q.push(-1); 70 continue; 71 } 72 73 for (AdjVNode *w = graph->G[v]; w; w = w->next) { 74 if (!vis[w->adjVer]) { 75 q.push(w->adjVer); 76 vis[w->adjVer] = true; 77 cnt++; 78 } 79 } 80 } 81 82 return cnt; 83 }
參考資料
浙江大學——數據結構:https://www.icourse163.org/course/ZJU-93001?tid=1461682474
[小白向]PTA 06-圖3 六度空間 (30分)DFS通過測試點4:https://blog.csdn.net/yine100320/article/details/107959177