強連通就是在一個有向圖中任何一個點都可以到達除這個點之外的所有點。
然後,在處理的時候,就可以把這一個子圖直接變成一個點。
比如說這張圖
就可以縮成這張圖
原圖中的
就是一個強連通
也就是說只要有環,就一定有一個強連通(其實自己一個也算一個強連通)
然後,就是找環的環節~~~~重點
找環,其實就是看看可不可以搜到之前被搜到的點。可是,如果有一個大環套了一個小環,而我們卻先搜到了這個小環,那麼搜完了之後還是要繼續搜下面的點,也就是說,我們一定要把這個點之後的點都搜完了之後纔可以下結論。
所以我們可以用表示這個點最多可以上到之前搜到過的哪一個點,這樣,如果不是他自己,那麼這裏一定有一個環——強連通分量。
那麼,要求出,就是最多能上到之前搜到過的哪一個點,就應該記錄搜到這個點的順序,存在中,然後,就可以直接取就好了。
另外,如果現在找到了一個點的,那麼它剛纔搜了一圈的點都可以變成一個強連通,所以,就可以用一個隊列來存儲剛剛搜過的點,直接把隊列中的數知道這個店取出來變成一個強連通就可以了。
代碼
int min(int x,int y){return x<y?x:y;}
int n,m;
struct zj{
int to,nex;
}e[maxm];
int k,head[maxn];
void add(int x,int y){
e[++k]=(zj){y,head[x]};head[x]=k;
}
int dfn[maxn],low[maxn],stack[maxn],top,scc[maxn],scct,cnt;
void tarjan(int x){
dfn[x]=low[x]=++cnt;//開始時一個點對多能達到的點就是他本身
stack[top++]=x;//進入隊列
for(int i=head[x];i;i=e[i].nex){
int v=e[i].to;
if(!dfn[v]){//這個點還沒有搜過
tarjan(v);//繼續搜這個點
low[x]=min(low[x],low[v]);//取最小值
}
else if(!scc[v])low[x]=min(low[x],low[v]);//注意要下一個點沒有成爲一個強連通才可以
}
if(dfn[x]==low[x]){//有一個強連通
scct++;//強連通的序號
while(stack[top-1]!=x){//出隊
scc[stack[top-1]]=scct;
top--;
}
scc[stack[top-1]]=scct;//剩下最後一個他自己
top--;
}
}