双向dfs求强连通分量

1.以任意一个点为源点,进行dfs,并将记录经过点的时间戳,时间戳逐渐增加。
2.进行dfs后,将图中的边的方向反向。寻找时间戳最小的点为源点(就是上面源点)进行dfs。这时,它所能达到的点集就是一个连通分量。并记录搜索过的点
3.在没有搜索过的点中以时间戳最小的点为源点,继续dfs,搜索结果同上
4.不断重复3,直到所有点都搜索过。
这个算法的意思就是如果某个点在边反向之前能到达,在边反向之后也能到达,那就说明这两个点是强连通的。
我是照着挑战程序设计竞赛抄的模板。书上说的是“给每个点标号,随着dfs进行标号变小”的意思。这里的标号应当是指其在经过dfs后,每个点在vs容器中的位置。每次从标号较大的点进行搜索,这里标号较大的点肯定是某连通分量树的根节点。我尝试了下相反的方法,即随着dfs进行标号变大的方法,结果证明我的想法是错的。如1<->2<-3。这时结果就是错的。代码就是我下面的第二个代码。

#include
#include
using namespace std;
#define mem(arr,a) memset(arr,a,sizeof(arr))
#define N 10000+10
int v;
vector<int>G[N];
vector<int>rG[N];
vector<int>vs;
bool used[N];
int cmp[N];
int n, m;
void add(int from, int to){
    G[from].push_back(to);
    rG[to].push_back(from);
}
void dfs(int v){
    used[v] = true;
    for (int i = 0; i < G[v].size(); i++){
        if (!used[G[v][i]])dfs(G[v][i]);
    }
    vs.push_back(v);
}
void rdfs(int v, int k){
    used[v] = true;
    cmp[v] = k;
    for (int i = 0; i < rG[v].size(); i++){
        if (!used[rG[v][i]])rdfs(rG[v][i], k);
    }
}
int scc(){
    mem(used, 0);
    vs.clear();
    for (int v = 1; v <= n; v++){
        if (!used[v])
            dfs(v);
    }
    mem(used, 0);
    int k = 0;
    for (int i = vs.size() - 1; i >= 0; i--){
        if (!used[vs[i]])
            rdfs(vs[i], k++);
    }
    return k;

}
int main(){

    while (cin >> n >> m){
        if (!n&&!m)break;
        for (int i = 0; i <= n; i++){
            G[i].clear();
            rG[i].clear();
        }
        while (m--){
            int a, b; cin >> a >> b;
            add(a, b);
        }

        if (scc() == 1)cout << "Yes" << endl;
        else cout << "No" << endl;
    }
}

下面是错的,我也在思考为啥

#include
#include
using namespace std;
#define mem(arr,a) memset(arr,a,sizeof(arr))
#define N 10000+10
int v;
vector<int>G[N];
vector<int>rG[N];
vector<int>vs;
bool used[N];
int cmp[N];
int n, m;
void add(int from, int to){
    G[from].push_back(to);
    rG[to].push_back(from);
}
void dfs(int v){
    used[v] = true;
    vs.push_back(v);
    for (int i = 0; i < G[v].size(); i++){
        if (!used[G[v][i]])dfs(G[v][i]);
    }

}
void rdfs(int v, int k){
    used[v] = true;
    cmp[v] = k;
    for (int i = 0; i < rG[v].size(); i++){
        if (!used[rG[v][i]])rdfs(rG[v][i], k);
    }
}
int scc(){
    mem(used, 0);
    vs.clear();
    for (int v = 1; v <= n; v++){
        if (!used[v])
            dfs(v);
    }
    mem(used, 0);
    int k = 0;
    for (int i = 0; i < vs.size();i++)
    {
        if (!used[vs[i]])
            rdfs(vs[i], k++);
    }
    return k;

}
int main(){

    while (cin >> n >> m){
        if (!n&&!m)break;
        for (int i = 0; i <= n; i++){
            G[i].clear();
            rG[i].clear();
        }
        while (m--){
            int a, b; cin >> a >> b;
            add(a, b);
        }

        if (scc() == 1)cout << "Yes" << endl;
        else cout << "No" << endl;
    }
}

你可能感兴趣的:(图论之连通分量)