圖論複習之強連通分量以及縮點—Tarjan演算法
在一個圖的子圖中,任意兩個點相互可達,也就是存在互通的路徑,那麼這個子圖就是強連通分量。(如果一個有向圖的任意兩個點相互可達,那麼這個圖就稱為強連通圖)。
【性質】
如果u是某個強連通分量的根,那麼:
(1)u不存在路徑可以返回到它的祖先。
(2)u的子樹也不存在路徑可以返回到u的祖先。
【演算法描述】
(1)我們先對每一個頂點判斷,如果已經被執行過了,則不動,否則進行擴充
(2)對於每一個點,我們設2個值來表示(dfn和low){low[i]表示結點i的根結點的dfn值},dfn則是結點i的時間戳
(3)在第一次順序遍歷時,dfn[u]=low[u]=++cnt,初始化序列
(4)然後將遍歷到的點壓入棧,並且置為已做過
(5)在整個圖中列舉一條以u為右端點的邊(from,to),然後判斷左端點是否已經入棧,如果已經入棧,則修改low[u]:low[u]=max(low[u],dfn(to)),如果沒入棧,則對其進行遞迴處理,返回時low[u]=min(low[u],low[to])
(6)當出現dfn[u]==low[u]時,則表明已經出現一個強聯通分量,依次彈出並消除標記即可,此時cnt++,則該作用為統計聯通塊數量
【總結】
上述演算法時間複雜度O(N+M)
【例題】
codevs2822《愛在心中》
“每個人都擁有一個夢,即使彼此不相同,能夠與你分享,無論失敗成功都會感動。愛因為在心中,平凡而不平庸,世界就像迷宮,卻又讓我們此刻相逢Our Home。”
在愛的國度裡有N個人,在他們的心中都有著一個愛的名單,上面記載著他所愛的人(不會出現自愛的情況)。愛是具有傳遞性的,即如果A愛B,B愛C,則A也愛C。
如果有這樣一部分人,他們彼此都相愛,則他們就超越了一切的限制,用集體的愛化身成為一個愛心天使。
現在,我們想知道在這個愛的國度裡會出現多少愛心天使。而且,如果某個愛心天使被其他所有人或愛心天使所愛則請輸出這個愛心天使是由哪些人構成的,否則輸出-1。
第1行,兩個數N、M,代表愛的國度裡有N個人,愛的關係有M條。
第2到第M+1行,每行兩個數A、B,代表A愛B。
第1行,一個數,代表愛的國度裡有多少愛心天使。
第2行,如果某個愛心天使被其他所有人和愛心天使所愛則請輸出這個愛心天使是由哪些人構成的(從小到大排序),否則輸出-1。
樣例輸入1:
6 7
1 2
2 3
3 2
4 2
4 5
5 6
6 4
樣例輸入2:
3 3
1 2
2 1
2 3
樣例輸出1:
2
2 3
樣例輸出2:
1
-1
然而第二問顯然就不是那麼簡單了,題目要求我們找出容量為n-1的聯通塊,這裡要注意:
不能直接計算,因為你的圖已經被第一問毀了。所以要再次構圖
然後統計每個點的“爸爸”以及每個點的強聯通分量個數
再尋找為n的即可,然後依次輸出
時間複雜度O(4N+M)
程式碼:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int MaxN=1001;
int s[MaxN],top;
int n,m;
int ans;//聯通塊個數
int dfn[MaxN],low[MaxN];
bool vis[MaxN],inq[MaxN];
int belong[MaxN];
int head[MaxN],hav[MaxN],h[MaxN];
struct graph{
int to,next;
}G[MaxN],R[MaxN];
int cnt1=0,cnt2=0;
inline int minx(int a,int b){return a<b?a:b;}
void addedge(int u,int v){
G[++cnt1].to=v;
G[cnt1].next=head[u];
head[u]=cnt1;
}
void tarjan(int u){
dfn[u]=low[u]=++cnt2;
s[++top]=u;
vis[u]=inq[u]=true;
for(int i=head[u];i!=0;i=G[i].next){
if(!dfn[G[i].to]){
tarjan(G[i].to);
low[u]=minx(low[u],low[G[i].to]);
}
else
if(inq[G[i].to])
low[u]=minx(low[u],dfn[G[i].to]);
}
if(dfn[u]==low[u]){
ans++;
int j=0;
while(j!=u){
j=s[top--];
inq[j]=false;
belong[j]=ans;
++hav[ans];
}
}
}
void rebuild(){
int i,j,cnt=0;
for(i=1;i<=n;i++)
for(j=head[i];j!=0;j=G[j].next)
if(belong[G[j].to]!=belong[i])
{
R[++cnt].to=belong[G[j].to];
R[cnt].next=h[belong[i]];
h[belong[i]]=cnt;
}
}
void work(){
int i,ans1=0;
for(i=1;i<=n;i++){
if(!vis[i])
tarjan(i);
}
rebuild();
for(i=1;i<=ans;i++)
if(hav[i]>1)ans1++;
printf("%d\n",ans1);
ans1=-1;
for(i=1;i<=ans;i++)
if(!h[i]){
if(ans1!=-1 || hav[i]==1)
{ans1=-1;break;}
else
ans1=i;
}
if(ans1==-1)printf("%d",ans1);
else {
for(i=1;i<=n;i++)
if(belong[i]==ans1)printf("%d ",i);
}
}
int main(){
scanf("%d %d",&n,&m);
int i,u,v;
memset(vis,false,sizeof(vis));
for(i=1;i<=m;i++){
scanf("%d %d",&u,&v);
addedge(u,v);
}
work();
return 0;
}
-----------------------------------------thanks for watching------------------------------------------
相關文章
- 圖之強連通、強連通圖、強連通分量 Tarjan演算法演算法
- 圖論——強連通分量(Tarjan演算法)圖論演算法
- 【模板】tarjan 強連通分量縮點
- 強連通分量及縮點tarjan演算法解析演算法
- POJ 2186 Popular Cows(強連通分量縮點,Tarjan演算法)演算法
- 強連通分量(Tarjan演算法)演算法
- 強連通分量與縮點(Tarjan演算法)(洛谷P3387)演算法
- Tarjan演算法(強連通分量分解)演算法
- 演算法學習之路|強連通分量+縮點演算法
- Tarjan 求有向圖的強連通分量
- 【演算法學習】tarjan 強連通、點雙、邊雙及其縮點 重磅來襲!!!!演算法
- Tarjan演算法求強連通分量總結演算法
- 強連通分量-tarjan演算法模板詳解演算法
- HDU 2767 Proving Equivalences Tarjan 強連通縮點UI
- 尋找圖的強連通分量:tarjan演算法簡單理解演算法
- kosaraju 和 tarjan演算法詳解(強連通分量)演算法
- Tarjan演算法三大應用之強連通分量演算法
- 強連通------tarjan演算法詳解及與縮點聯合運用演算法
- 強連通分量及縮點 演算法解析及例題演算法
- Trajan演算法(強連通+縮點)演算法
- 連通圖與Tarjan演算法演算法
- 20行程式碼實現,使用Tarjan演算法求解強連通分量行程演算法
- Tarjan演算法_縮點演算法
- HDU2767Proving Equivalences[強連通分量 縮點]UI
- 連通圖演算法詳解之① :Tarjan 和 Kosaraju 演算法演算法
- 強連通演算法--Tarjan個人理解+詳解演算法
- tarjan演算法求scc & 縮點演算法
- 強連通分量
- 強連通圖的演算法演算法
- 【筆記】tarjian演算法 求強連通分量筆記演算法
- 圖論-有向圖縮點圖論
- 求有向圖的強連通分量(c語言版)C語言
- 圖論系列之「深度優先遍歷及聯通分量」圖論
- Tarjan縮點題單 刷題題解
- 【Tarjan SCC 加邊使得所有圖聯通 至少選取多少個點能圖聯通 】Network of Schools加強版.md
- POJ 3592 Instantaneous Transference 圖論演算法tarjan+spfa圖論演算法
- [複習] 圖連通性
- tarjan縮點-受歡迎的牛-筆記筆記