圖論複習之強連通分量以及縮點—Tarjan演算法

斷橋_殘雪發表於2014-10-20
圖論複習之強連通分量以及縮點—Tarjan演算法
                                by RtPYH
------------------------------------------------------------------------------------------------
【強連通分量以及連通子圖】
  #define#

    在一個圖的子圖中,任意兩個點相互可達,也就是存在互通的路徑,那麼這個子圖就是強連通分量。(如果一個有向圖的任意兩個點相互可達,那麼這個圖就稱為強連通圖)。

【性質】

   如果u是某個強連通分量的根,那麼:

1u不存在路徑可以返回到它的祖先

2u的子樹也不存在路徑可以返回到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《愛在心中》

 

題目描述 Description

“每個人都擁有一個夢,即使彼此不相同,能夠與你分享,無論失敗成功都會感動。愛因為在心中,平凡而不平庸,世界就像迷宮,卻又讓我們此刻相逢Our Home。”

在愛的國度裡有N個人,在他們的心中都有著一個愛的名單,上面記載著他所愛的人(不會出現自愛的情況)。愛是具有傳遞性的,即如果A愛B,B愛C,則A也愛C。
如果有這樣一部分人,他們彼此都相愛,則他們就超越了一切的限制,用集體的愛化身成為一個愛心天使。
現在,我們想知道在這個愛的國度裡會出現多少愛心天使。而且,如果某個愛心天使被其他所有人或愛心天使所愛則請輸出這個愛心天使是由哪些人構成的,否則輸出-1。

輸入描述 Input Description

第1行,兩個數N、M,代表愛的國度裡有N個人,愛的關係有M條。
第2到第M+1行,每行兩個數A、B,代表A愛B。

輸出描述 Output Description

第1行,一個數,代表愛的國度裡有多少愛心天使。
第2行,如果某個愛心天使被其他所有人和愛心天使所愛則請輸出這個愛心天使是由哪些人構成的(從小到大排序),否則輸出-1。

樣例輸入 Sample Input

樣例輸入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

樣例輸出 Sample Output

樣例輸出1:

2
2 3

樣例輸出2:

1
-1

  很明顯,對於題目的第一問,我們只需要將給出的關係圖跑一遍裸的tarjan即可

  然而第二問顯然就不是那麼簡單了,題目要求我們找出容量為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------------------------------------------

相關文章