主要藉助這道比較裸的題來講一下tarjan這種演算法

tarjan是一種求解有向圖強連通分量的線性時間的演算法。(用dfs來實現)

如果兩個頂點可以相互通達,則稱兩個頂點強連通。如果有向圖G的每兩個頂點都強連通,稱G是一個強連通圖。有向圖的極大強連通子圖,稱為強連通分量。

在上面這張有向圖中1,2,3,4形成了一個強連通分量,而1,2,4,和1,3,4並不是(因為它們並不是極大強連通子圖)。

tarjan是用dfs來實現的(用了tarjan後我們就可以對圖進行縮點(當然這道裸題用不到))

這道題只要找到最大的強連通分量,並且把將該強連通分量的序號從小到大輸出(如果有一樣大的強連通分量,那麼輸出含有更小的點的那一個)

下面來道題:(tarjan程式碼加註釋往下拉)

題目

題目描述 Description
在幻想鄉,上白澤慧音是以知識淵博聞名的老師。春雪異變導致人間之裡的很多道路都被大雪堵塞,使有的學生不能順利地到達慧音所在的村莊。因此慧音決定換一個能夠聚集最多人數的村莊作為新的教學地點。人間之裡由N個村莊(編號為1..N)和M條道路組成,道路分為兩種一種為單向通行的,一種為雙向通行的,分別用1和2來標記。如果存在由村莊A到達村莊B的通路,那麼我們認為可以從村莊A到達村莊B,記為(A,B)。當(A,B)和(B,A)同時滿足時,我們認為A,B是絕對連通的,記為<A,B>。絕對連通區域是指一個村莊的集合,在這個集合中任意兩個村莊X,Y都滿足<X,Y>。現在你的任務是,找出最大的絕對連通區域,並將這個絕對連通區域的村莊按編號依次輸出。若存在兩個最大的,輸出字典序最小的,比如當存在1,,4和2,,6這兩個最大連通區域時,輸出的是1,,。 輸入描述 Input Description
第1行:兩個正整數N,M 第2..M+1行:每行三個正整數a,b,t, t = 1表示存在從村莊a到b的單向道路,t = 2表示村莊a,b之間存在雙向通行的道路。保證每條道路只出現一次。 輸出描述 Output Description
第1行: 1個整數,表示最大的絕對連通區域包含的村莊個數。 第2行:若干個整數,依次輸出最大的絕對連通區域所包含的村莊編號。 樣例輸入 Sample Input 樣例輸出 Sample Output 資料範圍及提示 Data Size & Hint
對於60%的資料:N <= 200且M <= , 對於100%的資料:N <= ,000且M <= ,

很明顯這道題需要用到tarjan

#include<stdio.h>
#include<algorithm>
using namespace std;
int dfn[], low[], stack[], j, number, n, m, x, y, w, hh[], cnt, top, c, q, ans, yy[];
int color[], u, num, p[];
bool d[];
struct node
{
int next, z, e;
} b[];
void add(int aa, int bb)//鄰接表
{
b[++cnt].e = bb;
b[cnt].next = hh[aa];
hh[aa] = cnt;
}

tarjan程式碼:

int tarjan(int k)
{
int i;
dfn[k] = low[k] = ++number;//dfn記錄的是訪問此節點的真實時間,low記錄的是
stack[++top] = k;將當前點入棧
d[k] = true;//這是表示點k的狀態
for(i = hh[k]; i != ; i = b[i].next)
{
if(!dfn[b[i].e])如果當前節點沒有訪問過就繼續搜
{
tarjan(b[i].e);
low[k] = min(low[k], low[b[i].e]);
}
else if(d[b[i].e] == true)
{
low[k] = min(low[k], dfn[b[i].e]);
//當然也可以寫成low[k]=min(low[k],low[b[i].e]);
}
}
if(dfn[k] == low[k])//如果該點是強連通分量的根,也就是說我們已經找到了一個強連通分量,就開始彈棧
{
color[k] = ++num;//把該強連通分量上的點全部染成同一種顏色
while()
{
p[num]++;//記錄該強連通分量上的點
d[stack[top]] = false;//棧頂元素出棧
color[stack[top--]] = num;將棧頂元素的顏色染成當前該強連通分量的顏色
if(stack[top + ] == k)break;//因為根肯定是當前強連通分量上最先訪問,也就是最先入站的,所以彈出了根代表該強連通分量上的已全部彈出
}
}
return ;
}
int main()
{
int i;
scanf("%d %d", &n, &m);
u = ;
for(i = ; i <= m; i++)
{
scanf("%d %d %d", &x, &y, &w);
if(w == )add(x, y);//建邊
else
{
add(x, y);
add(y, x);
}
}
for(j = ; j <= n; j++)
{
if(!dfn[j])tarjan(j);//如果當前點沒有被搜過,就從當前點進行深搜
}
for(i = ; i <= n; i++)
{
if(p[color[i]] > ans)ans = p[color[i]], u = i;
}
printf("%d\n", ans);
for(i = ; i <= n; i++)
{
if(color[i] == color[u])printf("%d ", i);
}
return ;
}