主要借助這道比較裸的題來講一下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; }