主要借助這道比較裸的題來講一下tarjan這種算法
tarjan是一種求解有向圖強連通分量的線性時間的算法。(用dfs來實現)
如果兩個頂點可以相互通達,則稱兩個頂點強連通。如果有向圖G的每兩個頂點都強連通,稱G是一個強連通圖。有向圖的極大強連通子圖,稱為強連通分量。
在上面這張有向圖中1,2,3,4形成了一個強連通分量,而1,2,4,和1,3,4並不是(因為它們並不是極大強連通子圖)。
tarjan是用dfs來實現的(用了tarjan後我們就可以對圖進行縮點(當然這道裸題用不到))
這道題只要找到最大的強連通分量,並且把將該強連通分量的序號從小到大輸出(如果有一樣大的強連通分量,那麼輸出含有更小的點的那一個)
下面來道題:(tarjan代碼加注釋往下拉)

很明顯這道題需要用到tarjan
#include<stdio.h>
#include<algorithm>
using namespace std;
int dfn[500000], low[500000], stack[900000], j, number, n, m, x, y, w, hh[600000], cnt, top, c, q, ans, yy[100000];
int color[400000], u, num, p[400000];
bool d[500000];
struct node
{
int next, z, e;
} b[110000];
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 != 0; 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(1)
{
p[num]++;//記錄該強連通分量上的點
d[stack[top]] = false;//棧頂元素出棧
color[stack[top--]] = num;將棧頂元素的顏色染成當前該強連通分量的顏色
if(stack[top + 1] == k)break;//因為根肯定是當前強連通分量上最先訪問,也就是最先入站的,所以彈出了根代表該強連通分量上的已全部彈出
}
}
return 0;
}
int main()
{
int i;
scanf("%d %d", &n, &m);
u = 1;
for(i = 1; i <= m; i++)
{
scanf("%d %d %d", &x, &y, &w);
if(w == 1)add(x, y);//建邊
else
{
add(x, y);
add(y, x);
}
}
for(j = 1; j <= n; j++)
{
if(!dfn[j])tarjan(j);//如果當前點沒有被搜過,就從當前點進行深搜
}
for(i = 1; i <= n; i++)
{
if(p[color[i]] > ans)ans = p[color[i]], u = i;
}
printf("%d\n", ans);
for(i = 1; i <= n; i++)
{
if(color[i] == color[u])printf("%d ", i);
}
return 0;
}