1. 割點與連通度
在無向連通圖中,刪除一個頂點v及其相連的邊后,原圖從一個連通分量變成了兩個或多個連通分量,則稱頂點v為割點,同時也稱關節點(Articulation Point)。一個沒有關節點的連通圖稱為重連通圖(biconnected graph)。若在連通圖上至少刪去k 個頂點才能破壞圖的連通性,則稱此圖的連通度為k。
關節點和重連通圖在實際中較多應用。顯然,一個表示通信網絡的圖的連通度越高,其系統越可靠,無論是哪一個站點出現故障或遭到外界破壞,都不影響系統的正常工作;又如,一個航空網若是重連通的,則當某條航線因天氣等某種原因關閉時,旅客仍可從別的航線繞道而行;再如,若將大規模的集成電路的關鍵線路設計成重連通的話,則在某些元件失效的情況下,整個片子的功能不受影響,反之,在戰爭中,若要摧毀敵方的運輸線,僅需破壞其運輸網中的關節點即可。
簡單的例子
(a)中G7 是連通圖,但不是重連通圖。圖中有三個關節點A、B 和G 。若刪去頂點B 以及所有依附頂點B 的邊,G7 就被分割成三個連通分量{A、C、F、L、M、J}、{G、H、I、K}和{D、E}。類似地,若刪去頂點A 或G 以及所依附於它們的邊,則G7 被分割成兩個連通分量。
2. 求割點的方法
暴力的方法:
- 依次刪除每一個節點v
- 用DFS(或BFS)判斷還是否連通
- 再把節點v加入圖中
若用鄰接表(adjacency list),需要做V次DFS,時間復雜度為O(V∗(V+E))。(題外話:我在面試實習的時候,只想到暴力方法;面試官提示只要一次DFS就就可以找到割點,當時死活都沒想出來)。
有關DFS搜索樹的概念
在介紹算法之前,先介紹幾個基本概念
- DFS搜索樹:用DFS對圖進行遍歷時,按照遍歷次序的不同,我們可以得到一棵DFS搜索樹,如圖(b)所示。
- 樹邊:(在[2]中稱為父子邊),在搜索樹中的實線所示,可理解為在DFS過程中訪問未訪問節點時所經過的邊。
- 回邊:(在[2]中稱為返祖邊、后向邊),在搜索樹中的虛線所示,可理解為在DFS過程中遇到已訪問節點時所經過的邊。
基於DFS的算法
該算法是R.Tarjan發明的。觀察DFS搜索樹,我們可以發現有兩類節點可以成為割點:
- 對根節點u,若其有兩棵或兩棵以上的子樹,則該根結點u為割點;
- 對非葉子節點u(非根節點),若其子樹的節點均沒有指向u的祖先節點的回邊,說明刪除u之后,根結點與u的子樹的節點不再連通;則節點u為割點。
對於根結點,顯然很好處理;但是對於非葉子節點,怎么去判斷有沒有回邊是一個值得深思的問題。
我們用dfn[u]
記錄節點u在DFS過程中被遍歷到的次序號,low[u]
記錄節點u或u的子樹通過非父子邊追溯到最早的祖先節點(即DFS次序號最小),那么low[u]的計算過程如下:
下表給出圖(a)對應的dfn與low數組值。
i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
vertex | A | B | C | D | E | F | G | H | I | J | K | L | M |
dfn[i] | 1 | 5 | 12 | 10 | 11 | 13 | 8 | 6 | 9 | 4 | 7 | 2 | 3 |
low[i] | 1 | 1 | 1 | 5 | 5 | 1 | 5 | 5 | 8 | 2 | 5 | 1 | 1 |
對於情況2,當(u,v)為樹邊且low[v] >= dfn[u]
時,節點u才為割點。該式子的含義:以節點v為根的子樹所能追溯到最早的祖先節點要么為v要么為u。
代碼實現
1 void dfs(int u) { 2 //記錄dfs遍歷次序 3 static int counter = 0; 4 5 //記錄節點u的子樹數 6 int children = 0; 7 8 ArcNode *p = graph[u].firstArc; 9 visit[u] = 1; 10 11 //初始化dfn與low 12 dfn[u] = low[u] = ++counter; 13 14 for(; p != NULL; p = p->next) { 15 int v = p->adjvex; 16 17 //節點v未被訪問,則(u,v)為樹邊 18 if(!visit[v]) { 19 children++; 20 parent[v] = u; 21 dfs(v); 22 low[u] = min(low[u], low[v]); 23 //case (1) 24 if(parent[u] == NIL && children > 1) { 25 printf("articulation point: %d\n", u); 26 } 27 //case (2) 28 if(parent[u] != NIL && low[v] >= dfn[u]) { 29 printf("articulation point: %d\n", u); 30 } 31 } 32 33 //節點v已訪問,則(u,v)為回邊 34 else if(v != parent[u]) { 35 low[u] = min(low[u], dfn[v]); 36 } 37 } 38 }
采用鄰接表存儲圖,該算法的時間復雜度應與DFS相同,為O(V+E)O(V+E)。
3. 參考資料
[1] see xidian, 圖的連通性—關節點和重連通分量.
[2] byvoid, 圖的割點、橋與雙連通分支.
[3] GeeksforGeeks, Articulation Points (or Cut Vertices) in a Graph.
僅供自己學習,原文from: https://www.cnblogs.com/en-heng/p/4002658.html
簡介:
割邊和割點的定義僅限於無向圖中。我們可以通過定義以蠻力方式求解出無向圖的所有割點和割邊,但這樣的求解方式效率低。Tarjan提出了一種快速求解的方式,通過一次DFS就求解出圖中所有的割點和割邊。
歡迎探討,如有錯誤敬請指正
如需轉載,請注明出處 http://www.cnblogs.com/nullzx/
1. 割點與橋(割邊)的定義
在無向圖中才有割邊和割點的定義
割點:無向連通圖中,去掉一個頂點及和它相鄰的所有邊,圖中的連通分量數增加,則該頂點稱為割點。
橋(割邊):無向聯通圖中,去掉一條邊,圖中的連通分量數增加,則這條邊,稱為橋或者割邊。
割點與橋(割邊)的關系:
1)有割點不一定有橋,有橋一定存在割點
2)橋一定是割點依附的邊。
下圖中頂點C為割點,但和C相連的邊都不是橋。
2. 暴力解決辦法解決求解割點集和割邊集
暴力法的原理就是通過定義求解割點和割邊。在圖中去掉某個頂點,然后進行DFS遍歷,如果連通分量增加,那么該頂點就是割點。如果在圖中去掉某條邊,然后進行DFS遍歷,如果連通分量增加,那么該邊就是割邊。對每個頂點或者每個邊進行一次上述操作,就可以求出這個圖的所有割點和割邊,我們稱之為這個圖的割點集和割邊集。
在具體的代碼實現中,並不需要真正刪除該頂點和刪除依附於該頂點所有邊。對於割點,我們只需要在DFS前,將該頂點對應是否已訪問的標記置為ture,然后從其它頂點為根進行DFS即可。對於割邊,我們只需要禁止從這條邊進行DFS后,如果聯通分量增加了,那么這條邊就是割邊。
3. Tarjan算法的原理
判斷一個頂點是不是割點除了從定義,還可以從DFS(深度優先遍歷)的角度出發。我們先通過DFS定義兩個概念。
假設DFS中我們從頂點U訪問到了頂點V(此時頂點V還未被訪問過),那么我們稱頂點U為頂點V的父頂點,V為U的孩子頂點。在頂點U之前被訪問過的頂點,我們就稱之為U的祖先頂點。
顯然如果頂點U的所有孩子頂點可以不通過父頂點U而訪問到U的祖先頂點,那么說明此時去掉頂點U不影響圖的連通性,U就不是割點。相反,如果頂點U至少存在一個孩子頂點,必須通過父頂點U才能訪問到U的祖先頂點,那么去掉頂點U后,頂點U的祖先頂點和孩子頂點就不連通了,說明U是一個割點。
上圖中的箭頭表示DFS訪問的順序(而不表示有向圖),對於頂點D而言,D的孩子頂點可以通過連通區域1紅色的邊回到D的祖先頂點C(此時C已被訪問過),所以此時D不是割點。
上圖中的連通區域2中的頂點,必須通過D才能訪問到D的祖先頂點,所以說此時D為割點。再次強調一遍,箭頭僅僅表示DFS的訪問順序,而不是表示該圖是有向圖。
這里我們還需要考慮一個特殊情況,就是DFS的根頂點(一般情況下是編號為0的頂點),因為根頂點沒有祖先頂點。其實根頂點是不是割點也很好判斷,如果從根頂點出發,一次DFS就能訪問到所有的頂點,那么根頂點就不是割點。反之,如果回溯到根頂點后,還有未訪問過的頂點,需要在鄰接頂點上再次進行DFS,根頂點就是割點。
4. Tarjan算法的實現細節
在具體實現Tarjan算法上,我們需要在DFS(深度優先遍歷)中,額外定義三個數組dfn[],low[],parent[]
4.1 dfn數組
dnf數組的下標表示頂點的編號,數組中的值表示該頂點在DFS中的遍歷順序(或者說時間戳),每訪問到一個未訪問過的頂點,訪問順序的值(時間戳)就增加1。子頂點的dfn值一定比父頂點的dfn值大(但不一定恰好大1,比如父頂點有兩個及兩個以上分支的情況)。在訪問一個頂點后,它的dfn的值就確定下來了,不會再改變。
4.2 low數組
low數組的下標表示頂點的編號,數組中的值表示DFS中該頂點不通過父頂點能訪問到的祖先頂點中最小的順序值(或者說時間戳)。
每個頂點初始的low值和dfn值應該一樣,在DFS中,我們根據情況不斷更新low的值。
假設由頂點U訪問到頂點V。當從頂點V回溯到頂點U時,
如果
dfn[v] < low[u]
那么
low[u] = dfn[v]
如果頂點U還有它分支,每個分支回溯時都進行上述操作,那么頂點low[u]就表示了不通過頂點U的父節點所能訪問到的最早祖先節點。
4.3 parent數組
parent[]:下標表示頂點的編號,數組中的值表示該頂點的父頂點編號,它主要用於更新low值的時候排除父頂點,當然也可以其它的辦法實現相同的功能。
4.4 一個具體的例子
現在我們來看一個例子,模仿程序計算各個頂點的dfn值和low值。下圖中藍色實線箭頭表示已訪問過的路徑,無箭頭虛線表示未訪問路徑。已訪問過的頂點用黃色標記,未訪問的頂點用白色標記,DFS當前正在處理的頂點用綠色表示。帶箭頭的藍色虛線表示DFS回溯時的返回路徑。
1)
當DFS走到頂點H時,有三個分支,我們假設我們先走H-I,然后走H-F,最后走H-J。從H訪問I時,頂點I未被訪問過,所以I的dfn和low都為9。根據DFS的遍歷順序,我們應該從頂點I繼續訪問。
2)
上圖表示由頂點I訪問頂點D,而此時發現D已被訪問,當從D回溯到I時,由於
dfn[D] < dfn[I]
說明D是I的祖先頂點,所以到現在為止,頂點I不經過父頂點H能訪問到的小時間戳為4。
3)
根據DFS的原理,我們從頂點I回到頂點H,顯然到目前為止頂點H能訪問到的最小時間戳也是4(因為我們到現在為止只知道能從H可以通過I訪問到D),所以low[H] = 4
4)
現在我們繼續執行DFS,走H-F路徑,發現頂點F已被訪問且dfn[F] < dfn[H],說明F是H的祖先頂點,但此時頂點H能訪問的最早時間戳是4,而F的時間戳是6,依據low值定義low[H]仍然為4。
5)
最后我們走H-J路徑,頂點J未被訪問過所以 dfn[J] = 10 low[J] = 10
6)
同理,由DFS訪問頂點B,dfn[J] > dfn[B],B為祖先頂點,頂點J不經過父頂點H能訪問到的最早時間戳就是dfn[B],即low[J] = 2
7)
我們從頂點J回溯到頂點H,顯然到目前為止頂點H能訪問到的最早時間戳就更新為2(因為我們到現在為止知道了能從H訪問到J),所以low[H] = 2
8)
根據DFS原理,我們從H回退到頂點E(H回退到G,G回退到F,F回退到E的過程省略),所經過的頂點都會更新low值,因為這些頂點不用通過自己的父頂點就可以和頂點B相連。當回溯到頂點E時,還有未訪問過的頂點,那么繼續進行E-K分支的DFS。
9)
從E-K分支訪問到頂點L時,頂點k和L的的dfn值和low值如圖上圖所示
10)
接着我們繼續回溯到了頂點D(中間過程有所省略),並更新low[D]
11)
最后,按照DFS的原理,我們回退到頂點A,並且求出來了每個頂點的dfn值和low值。
4.5 割點及橋的判定方法
割點:判斷頂點U是否為割點,用U頂點的dnf值和它的所有的孩子頂點的low值進行比較,如果存在至少一個孩子頂點V滿足low[v] >= dnf[u],就說明頂點V訪問頂點U的祖先頂點,必須通過頂點U,而不存在頂點V到頂點U祖先頂點的其它路徑,所以頂點U就是一個割點。對於沒有孩子頂點的頂點,顯然不會是割點。
橋(割邊):low[v] > dnf[u] 就說明V-U是橋
需要說明的是,Tarjan算法從圖的任意頂點進行DFS都可以得出割點集和割邊集。
從上圖的結果中我們可以看出,頂點B,頂點E和頂點K為割點,A-B以及E-K和K-L為割邊。
5.代碼實現:
測試:
------圖的鄰接表示法-------
0
: [
0
,
5
] [
0
,
1
] [
0
,
2
] [
0
,
6
]
1
: [
1
,
0
]
2
: [
2
,
0
]
3
: [
3
,
4
] [
3
,
5
]
4
: [
4
,
3
] [
4
,
6
] [
4
,
5
]
5
: [
5
,
0
] [
5
,
4
] [
5
,
3
]
6
: [
6
,
4
] [
6
,
7
] [
6
,
9
] [
6
,
0
]
7
: [
7
,
8
] [
7
,
6
]
8
: [
8
,
7
]
9
: [
9
,
12
] [
9
,
10
] [
9
,
11
] [
9
,
6
]
10
: [
10
,
9
]
11
: [
11
,
12
] [
11
,
9
]
12
: [
12
,
9
] [
12
,
11
]
-------圖中的割點-------
0
6
7
9
-------圖中的割邊-----
7
8
6
7
9
10
6
9
0
1
0
2
6. 參考內容
[1]. http://www.cnblogs.com/en-heng/p/4002658.html
[2]. http://blog.csdn.net/wtyvhreal/article/details/43530613
[3]. http://www.cppblog.com/ZAKIR/archive/2010/08/30/124869.html?opt=admin
[4].https://www.cnblogs.com/nullzx/p/7968110.html
Network(求割點)
http://poj.org/problem?id=1144
題目描述
A Telephone Line Company (TLC) is establishing a new telephone cable network. They are connecting several places numbered by integers from 1 to N. No two places have the same number. The lines are bidirectional and always connect together two places and in each place the lines end in a telephone exchange. There is one telephone exchange in each place. From each place it is possible to reach through lines every other place, however it need not be a direct connection, it can go through several exchanges. From time to time the power supply fails at a place and then the exchange does not operate. The officials from TLC realized that in such a case it can happen that besides the fact that the place with the failure is unreachable, this can also cause that some other places cannot connect to each other. In such a case we will say the place (where the failure occured) is critical. Now the officials are trying to write a program for finding the number of all such critical places. Help them
Input
The input file consists of several blocks of lines. Each block describes one network. In the first line of each block there is the number of places N < 100. Each of the next at most N lines contains the number of a place followed by the numbers of some places to which there is a direct line from this place. These at most N lines completely describe the network, i.e., each direct connection of two places in the network is contained at least in one row. All numbers in one line are separated by one space. Each block ends with a line containing just ‘0’. The last block has only one line with N = 0.
Output
The output contains for each block except the last in the input file one line containing the number of critical places.
Sample Input
5 5 1 2 3 4 0 6 2 1 3 5 4 6 2 0 0
Sample Output
1 2
題意翻譯:
多組數據讀入一個數n,n=0時退出,代表n個點的無向圖。下面若干行,每行第一個數為u,u=0時退出,后面若干個數v,代表和u相連的點v,遇到換行退出。對於每組數據,輸出圖的割頂個數(輸入較坑,所以詳細說明)。
解題思路:
無向圖求割點,測試模版,注意輸入字符串的處理。
代碼如下:

1 #include <stdio.h> 2 #include <iostream> 3 #include <string.h> 4 #include <algorithm> 5 #include <string> 6 #include <sstream> 7 using namespace std; 8 #define maxn 105 9 10 struct edge{ 11 int v; 12 int next; 13 }Edge[maxn*maxn];//邊的集合,邊最多為n*(n-1),故要開點數的平方 14 15 int node[maxn];//頂點集合 16 int DFN[maxn];//節點u搜索的序號(時間戳) 17 int LOW[maxn];//u或u的子樹能夠追溯到的最早的棧中節點的序號(時間戳) 18 int n;//n:點的個數 19 int cnt_edge;//邊的計數器 20 int Index;//序號(時間戳) 21 int fa[maxn]; //父親編號 22 int GD[maxn]; //是否為割點 23 24 25 void add_edge(int u,int v)//鄰接表存儲 26 { 27 Edge[cnt_edge].next=node[u]; 28 Edge[cnt_edge].v=v; 29 node[u]=cnt_edge++; 30 } 31 32 void tarjan(int u) 33 { 34 int children=0;//記錄節點u的子樹數 35 DFN[u]=LOW[u]=++Index; 36 for(int i=node[u];i!=-1;i=Edge[i].next) 37 { 38 int v=Edge[i].v; 39 if(!DFN[v])//如果點v沒被訪問 ,則(u,v)為樹邊 40 { 41 children++; 42 fa[v]=u; 43 tarjan(v); 44 LOW[u]=min(LOW[u],LOW[v]); 45 if(fa[u]==0&&children>1&&!GD[u]) 46 { 47 GD[u]=1; 48 } 49 if(fa[u]&&LOW[v]>=DFN[u]&&!GD[u]) 50 { 51 GD[u]=1; 52 } 53 54 } 55 else //如果點v已經被訪問過 56 { 57 if(DFN[v]) 58 LOW[u]=min(LOW[u],DFN[v]); 59 } 60 } 61 } 62 63 void init()//初始化,注意不要把n初始為0 64 { 65 cnt_edge=0; 66 Index=0; 67 memset(Edge,0,sizeof(Edge)); 68 memset(node,-1,sizeof(node)); 69 memset(DFN,0,sizeof(DFN)); 70 memset(LOW,0,sizeof(LOW)); 71 memset(fa,0,sizeof(fa)); 72 memset(GD,0,sizeof(GD)); 73 } 74 75 int main() 76 { 77 freopen("sample.txt","r",stdin); 78 while(~scanf("%d",&n)&&n) 79 { 80 init(); 81 char str[1010]; 82 getchar(); 83 while(cin.getline(str,1010)&&str[0]!='0')//gets不支持,用cin.getline來代替gets 84 { 85 istringstream is(str); 86 int u,v; 87 is>>u; 88 while(is>>v)//while中不要寫成is,要不最后一個信息會重復賦給v兩次 89 { 90 add_edge(u,v);//注意是無向邊 91 add_edge(v,u); 92 } 93 } 94 for(int i=1;i<=n;i++) 95 { 96 if(!DFN[i]) 97 { 98 tarjan(i); 99 } 100 } 101 int sum=0; 102 for(int i=1;i<=n;i++) 103 { 104 if(GD[i]) 105 sum++; 106 } 107 printf("%d\n",sum); 108 } 109 return 0; 110 }
UVA796 Critical Links —— 割邊(橋)
https://vjudge.net/problem/UVA-796
題目描述
In a computer network a link L, which interconnects two servers, is considered critical if there are at least two servers A and B such that all network interconnection paths between A and B pass through L. Removing a critical link generates two disjoint sub–networks such that any two servers of a sub–network are interconnected. For example, the network shown in figure 1 has three critical links that are marked bold: 0 -1, 3 - 4 and 6 - 7.
It is known that:
1. the connection links are bi–directional;
2. a server is not directly connected to itself;
3. two servers are interconnected if they are directly connected or if they are interconnected with the same server;
4. the network can have stand–alone sub–networks. Write a program that finds all critical links of a given computer network.
Input
The program reads sets of data from a text file. Each data set specifies the structure of a network and has the format: no of servers server0 (no of direct connections) connected server . . . connected server . . . serverno of servers (no of direct connections) connected server . . . connected server The first line contains a positive integer no of servers(possibly 0) which is the number of network servers. The next no of servers lines, one for each server in the network, are randomly ordered and show the way servers are connected. The line corresponding to serverk, 0 ≤ k ≤ no of servers − 1, specifies the number of direct connections of serverk and the servers which are directly connected to serverk. Servers are represented by integers from 0 to no of servers − 1.Input data are correct. The first data set from sample input below corresponds to the network in figure 1, while the second data set specifies an empty network.
Output
The result of the program is on standard output. For each data set the program prints the number of critical links and the critical links, one link per line, starting from the beginning of the line, as shown in the sample output below. The links are listed in ascending order according to their first element. The output for the data set is followed by an empty line.
Sample Input
8 0 (1) 1 1 (3) 2 0 3 2 (2) 1 3 3 (3) 1 2 4 4 (1) 3 7 (1) 6 6 (1) 7 5 (0) 0
Sample Output
3 critical links 0 - 1 3 - 4 6 - 7 0 critical links
題目大意:給你n個點,m條邊,問你有多少橋,並將其輸出。
思路:模板題,不過這一題有格式要求,要求從小到大輸出,並且每個數據后有空行。
問題和坑點:
這題一直wa,卡了我幾個小時,看了好幾個題解,感覺有的還沒我寫的好,但就是過不了,自閉了。
這題的輸入輸出樣例應該有點問題,我把while中的 if(n==0) break;刪了就能過了,但是刪了就和樣例輸出不同了。。。。但就是AC了????
代碼如下:

1 #include <stdio.h> 2 #include <iostream> 3 #include <string.h> 4 #include <algorithm> 5 #include <string> 6 #include <set> 7 #include <sstream> 8 using namespace std; 9 #define maxn 1010 10 11 struct edge{ 12 int v; 13 int next; 14 }Edge[maxn*maxn];//邊的集合 15 16 int node[maxn];//頂點集合 17 int DFN[maxn];//節點u搜索的序號(時間戳) 18 int LOW[maxn];//u或u的子樹能夠追溯到的最早的棧中節點的序號(時間戳) 19 int n;//n:點的個數 20 int cnt_edge;//邊的計數器 21 int Index;//序號(時間戳) 22 int fa[maxn]; //父親編號 23 set<pair<int,int> > st; 24 set<pair<int,int> >::iterator it; 25 26 void add_edge(int u,int v)//鄰接表存儲 27 { 28 Edge[cnt_edge].next=node[u]; 29 Edge[cnt_edge].v=v; 30 node[u]=cnt_edge++; 31 } 32 33 void tarjan(int u) 34 { 35 DFN[u]=LOW[u]=++Index; 36 for(int i=node[u];i!=-1;i=Edge[i].next) 37 { 38 int v=Edge[i].v; 39 if(!DFN[v])//如果點v沒被訪問 ,則(u,v)為樹邊 40 { 41 fa[v]=u; 42 tarjan(v); 43 LOW[u]=min(LOW[u],LOW[v]); 44 if(LOW[v]>DFN[u]) //判斷是否為橋 45 { 46 st.insert(make_pair(min(u,v),max(u,v))); 47 } 48 49 } 50 else if(v!=fa[u]) //如果點v已經被訪問過 51 { 52 if(DFN[v]) 53 LOW[u]=min(LOW[u],DFN[v]); 54 } 55 } 56 } 57 58 void init()//初始化,注意不要把n初始為0 59 { 60 cnt_edge=0; 61 Index=0; 62 memset(Edge,0,sizeof(Edge)); 63 memset(node,-1,sizeof(node)); 64 memset(DFN,0,sizeof(DFN)); 65 memset(LOW,0,sizeof(LOW)); 66 memset(fa,0,sizeof(fa)); 67 st.clear(); 68 } 69 70 int main() 71 { 72 freopen("sample.txt","r",stdin); 73 while(~scanf("%d",&n)) 74 { 75 init(); 76 int u,m; 77 for(int i=0;i<n;i++) 78 { 79 scanf("%d (%d)",&u,&m); 80 for(int j=0;j<m;j++) 81 { 82 int v; 83 scanf("%d",&v); 84 if(v<=u) 85 continue; 86 add_edge(u,v); 87 add_edge(v,u); 88 } 89 } 90 for(int i=0;i<n;i++) 91 { 92 if(!DFN[i]) 93 { 94 tarjan(i); 95 } 96 } 97 printf("%d critical links\n",st.size()); 98 for(it=st.begin();it!=st.end();it++) 99 { 100 printf("%d - %d\n",it->first,it->second); 101 } 102 printf("\n"); 103 // if(n==0) //刪了就能過 104 // break; 105 } 106 return 0; 107 }