標籤:class blog code tar 使用 2014
本題分兩步:
1 使用Tarjan演算法求所有最大子強連通圖,並且標誌出來
2 然後遍曆這些節點看是否有出射的邊,沒有的頂點所在的子強連通圖的所有點,都是解集。
Tarjan演算法就是模板演算法了。
這裡使用一個數組和一個標識號,就可以記錄這個頂點是屬於哪個子強連通圖的了。
然後使用DFS遞迴搜尋所有點及其邊,如果有邊的另一個頂點不屬於本子強連通圖,那麼就說明有出射的邊。
有難度的題目:
#include <stdio.h>#include <stdlib.h>#include <vector>#include <stack>using namespace std;const int MAX_VEC = 5005;int n, m;vector<int> gra[MAX_VEC];stack<int> stk;int dfn[MAX_VEC];//tarjan演算法記錄深度探索得到的標號int low[MAX_VEC];//tarjan演算法記錄回溯得到的最低頂點編號bool inStk[MAX_VEC];//記錄是否在棧裡面,也可以記錄是否被訪問過了int connectGrp[MAX_VEC];//標誌所屬的連通子表徵圖號 == connectNumint vecNum;//頂點標號int connectNum;//最大強連通子表徵圖號int out[MAX_VEC];//出度記錄void tarjan(int u){dfn[u] = low[u] = vecNum++;stk.push(u);inStk[u] = 1;for (int i = 0; i < (int)gra[u].size(); i++){int v = gra[u][i];if (!dfn[v]){tarjan(v);low[u] = min(low[u], low[v]);//兩處的不同,和含義不同}else if (inStk[v]) low[u] = min(dfn[v], low[u]);//兩處的不同}if (low[u] == dfn[u]){++connectNum;while (stk.size()){int v = stk.top(); stk.pop();inStk[v] = false;connectGrp[v] = connectNum;if (u == v) return;}}}void solveConnect(){memset(dfn, 0, sizeof(dfn));memset(low, 0, sizeof(low));memset(inStk, 0, sizeof(inStk));for (int i = 1; i <= n; i++){if (!dfn[i]) tarjan(i);}}void dfsCountOut(int u){inStk[u] = true;//記錄是否被訪問過了for (int i = 0; i < int(gra[u].size()); i++){int v = gra[u][i];if (connectGrp[u] != connectGrp[v]){out[connectGrp[u]]++;//這個組的出度增加; connectGrp[] == connectNum}if (!inStk[v]) dfsCountOut(v);//深度優先,不需要使用額外空間}}void countConnectOut(){memset(inStk, 0, sizeof(inStk));//這裡只記錄是否被訪問過的了。memset(out, 0, sizeof(out));for (int i = 1; i <= n; i++){if (!inStk[i]) dfsCountOut(i);}}int main(){int u, v;while (scanf("%d", &n) && n){connectNum = 0;vecNum = 1;scanf("%d", &m);for (int i = 1; i <= n; i++)gra[i].clear();while (stk.size()) stk.pop();//清零,重中之重for (int i = 0; i < m; i++){scanf("%d %d", &u, &v);gra[u].push_back(v);//有向圖}solveConnect();countConnectOut();for(int i = 1; i <= n; ++i)if(out[connectGrp[i]] == 0)printf("%d ", i);putchar('\n');}return 0;}